Initial import from Docker volume

This commit is contained in:
root
2025-12-26 13:11:43 +00:00
commit 4998dc066a
13336 changed files with 1767801 additions and 0 deletions

5
ir/ui/__init__.py Executable file
View File

@@ -0,0 +1,5 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from . import icon, menu, view
__all__ = ['menu', 'view', 'icon']

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

170
ir/ui/board.rnc Executable file
View File

@@ -0,0 +1,170 @@
namespace a = "http://relaxng.org/ns/compatibility/annotations/1.0"
board =
element board {
attlist.board,
(image
| separator
| label
| newline
| link
| notebook
| group
| hpaned
| vpaned
| action)*
}
attlist.board &= [ a:defaultValue = "4" ] attribute col { text }?
image = element image { attlist.image, empty }
attlist.image &= attribute name { text }
attlist.image &= [ a:defaultValue = "1" ] attribute colspan { text }?
attlist.image &=
[ a:defaultValue = "0" ] attribute yexpand { "0" | "1" }?
attlist.image &= [ a:defaultValue = "0" ] attribute yfill { "0" | "1" }?
attlist.image &=
[ a:defaultValue = "0" ] attribute xexpand { "0" | "1" }?
attlist.image &= [ a:defaultValue = "0" ] attribute xfill { "0" | "1" }?
attlist.image &= attribute help { text }?
attlist.image &= attribute states { text }?
separator = element separator { attlist.separator, empty }
attlist.separator &= [ a:defaultValue = "" ] attribute string { text }?
attlist.separator &= ( attribute name { text } | attribute name { text } )
attlist.separator &= attribute states { text }?
attlist.separator &=
[ a:defaultValue = "0.0" ] attribute xalign { text }?
attlist.separator &=
[ a:defaultValue = "1" ] attribute colspan { text }?
attlist.separator &=
[ a:defaultValue = "0" ] attribute yexpand { "0" | "1" }?
attlist.separator &=
[ a:defaultValue = "0" ] attribute yfill { "0" | "1" }?
attlist.separator &=
[ a:defaultValue = "0" ] attribute xexpand { "0" | "1" }?
attlist.separator &=
[ a:defaultValue = "0" ] attribute xfill { "0" | "1" }?
attlist.separator &= attribute help { text }?
label = element label { attlist.label, empty }
attlist.label &= [ a:defaultValue = "" ] attribute string { text }?
attlist.label &= ( attribute name { text } | attribute name { text } )
attlist.label &= attribute states { text }?
attlist.label &= [ a:defaultValue = "0.0" ] attribute xalign { text }?
attlist.label &= [ a:defaultValue = "0.5" ] attribute yalign { text }?
attlist.label &=
[ a:defaultValue = "0" ] attribute yexpand { "0" | "1" }?
attlist.label &= [ a:defaultValue = "0" ] attribute yfill { "0" | "1" }?
attlist.label &=
[ a:defaultValue = "0" ] attribute xexpand { "0" | "1" }?
attlist.label &= [ a:defaultValue = "1" ] attribute xfill { "0" | "1" }?
attlist.label &= [ a:defaultValue = "1" ] attribute colspan { text }?
attlist.label &= attribute help { text }?
newline = element newline { attlist.newline, empty }
attlist.newline &= attribute id { text }
link = element link { attlist.link, empty }
attlist.link &= attribute name { text }
attlist.link &= attribute id { text }?
attlist.link &= [ a:defaultValue = "1" ] attribute colspan { text }?
attlist.link &= attribute states { text }?
attlist.link &= attribute icon { text }?
attlist.link &= [ a:defaultValue = "show" ] attribute empty { "show" | "hide" }?
notebook = element notebook { attlist.notebook, page* }
attlist.notebook &= [ a:defaultValue = "4" ] attribute colspan { text }?
page =
element page {
attlist.page,
(image
| separator
| label
| newline
| link
| notebook
| group
| hpaned
| vpaned
| action)*
}
attlist.page &= attribute angle { text }?
attlist.page &=
[ a:defaultValue = "Unknown" ] attribute string { text }?
attlist.page &= [ a:defaultValue = "4" ] attribute col { text }?
attlist.page &= ( attribute name { text } | attribute id { text } )
group =
element group {
attlist.group,
(image
| separator
| label
| newline
| link
| notebook
| group
| hpaned
| vpaned
| action)*
}
attlist.group &= attribute string { text }?
attlist.group &= [ a:defaultValue = "1" ] attribute colspan { text }?
attlist.group &=
[ a:defaultValue = "0" ] attribute yexpand { "0" | "1" }?
attlist.group &= [ a:defaultValue = "1" ] attribute yfill { "0" | "1" }?
attlist.group &= [ a:defaultValue = "0.5" ] attribute yalign { text }?
attlist.group &=
[ a:defaultValue = "0" ] attribute xexpand { "0" | "1" }?
attlist.group &= [ a:defaultValue = "1" ] attribute xfill { "0" | "1" }?
attlist.group &= [ a:defaultValue = "0.5" ] attribute xalign { text }?
attlist.group &= [ a:defaultValue = "1" ] attribute rowspan { text }?
attlist.group &= [ a:defaultValue = "4" ] attribute col { text }?
attlist.group &= attribute id { text }
hpaned = element hpaned { attlist.paned, child* }
vpaned = element vpaned { attlist.paned, child* }
attlist.paned &= [ a:defaultValue = "4" ] attribute colspan { text }?
attlist.paned &= attribute position { text }?
attlist.paned &= attribute id { text }
child =
element child {
attlist.child,
(image
| separator
| label
| newline
| link
| notebook
| group
| hpaned
| vpaned
| action)*
}
attlist.child &= attribute id { text }
data = element data { attlist.data, xpath+ }
attlist.data &= empty
xpath = element xpath { attlist.xpath,
(image
| separator
| label
| newline
| link
| notebook
| group
| hpaned
| vpaned
| action
| page
| child
| board
)*
}
attlist.xpath &= attribute expr { text }
attlist.xpath &=
[ a:defaultValue = "inside" ]
attribute position { "inside" | "replace" | "replace_attributes" | "after" | "before" }?
action = element action { attlist.action, empty }
attlist.action &= attribute name { text }
attlist.action &= [ a:defaultValue = "1" ] attribute colspan { text }?
label |= notAllowed
start = data | board | label

739
ir/ui/board.rng Executable file
View File

@@ -0,0 +1,739 @@
<?xml version="1.0" encoding="UTF-8"?>
<grammar xmlns="http://relaxng.org/ns/structure/1.0"
xmlns:a="http://relaxng.org/ns/compatibility/annotations/1.0">
<define name="board">
<element>
<name ns="">board</name>
<ref name="attlist.board"/>
<zeroOrMore>
<choice>
<ref name="image"/>
<ref name="separator"/>
<ref name="label"/>
<ref name="newline"/>
<ref name="link"/>
<ref name="notebook"/>
<ref name="group"/>
<ref name="hpaned"/>
<ref name="vpaned"/>
<ref name="action"/>
</choice>
</zeroOrMore>
</element>
</define>
<define name="attlist.board" combine="interleave">
<optional>
<attribute a:defaultValue="4">
<name ns="">col</name>
<text/>
</attribute>
</optional>
</define>
<define name="image">
<element>
<name ns="">image</name>
<ref name="attlist.image"/>
<empty/>
</element>
</define>
<define name="attlist.image" combine="interleave">
<attribute>
<name ns="">name</name>
<text/>
</attribute>
</define>
<define name="attlist.image" combine="interleave">
<optional>
<attribute a:defaultValue="1">
<name ns="">colspan</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.image" combine="interleave">
<optional>
<attribute a:defaultValue="0">
<name ns="">yexpand</name>
<choice>
<value>0</value>
<value>1</value>
</choice>
</attribute>
</optional>
</define>
<define name="attlist.image" combine="interleave">
<optional>
<attribute a:defaultValue="0">
<name ns="">yfill</name>
<choice>
<value>0</value>
<value>1</value>
</choice>
</attribute>
</optional>
</define>
<define name="attlist.image" combine="interleave">
<optional>
<attribute a:defaultValue="0">
<name ns="">xexpand</name>
<choice>
<value>0</value>
<value>1</value>
</choice>
</attribute>
</optional>
</define>
<define name="attlist.image" combine="interleave">
<optional>
<attribute a:defaultValue="0">
<name ns="">xfill</name>
<choice>
<value>0</value>
<value>1</value>
</choice>
</attribute>
</optional>
</define>
<define name="attlist.image" combine="interleave">
<optional>
<attribute>
<name ns="">help</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.image" combine="interleave">
<optional>
<attribute>
<name ns="">states</name>
<text/>
</attribute>
</optional>
</define>
<define name="separator">
<element>
<name ns="">separator</name>
<ref name="attlist.separator"/>
<empty/>
</element>
</define>
<define name="attlist.separator" combine="interleave">
<optional>
<attribute a:defaultValue="">
<name ns="">string</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.separator" combine="interleave">
<choice>
<attribute>
<name ns="">name</name>
<text/>
</attribute>
<attribute>
<name ns="">name</name>
<text/>
</attribute>
</choice>
</define>
<define name="attlist.separator" combine="interleave">
<optional>
<attribute>
<name ns="">states</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.separator" combine="interleave">
<optional>
<attribute a:defaultValue="0.0">
<name ns="">xalign</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.separator" combine="interleave">
<optional>
<attribute a:defaultValue="1">
<name ns="">colspan</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.separator" combine="interleave">
<optional>
<attribute a:defaultValue="0">
<name ns="">yexpand</name>
<choice>
<value>0</value>
<value>1</value>
</choice>
</attribute>
</optional>
</define>
<define name="attlist.separator" combine="interleave">
<optional>
<attribute a:defaultValue="0">
<name ns="">yfill</name>
<choice>
<value>0</value>
<value>1</value>
</choice>
</attribute>
</optional>
</define>
<define name="attlist.separator" combine="interleave">
<optional>
<attribute a:defaultValue="0">
<name ns="">xexpand</name>
<choice>
<value>0</value>
<value>1</value>
</choice>
</attribute>
</optional>
</define>
<define name="attlist.separator" combine="interleave">
<optional>
<attribute a:defaultValue="0">
<name ns="">xfill</name>
<choice>
<value>0</value>
<value>1</value>
</choice>
</attribute>
</optional>
</define>
<define name="attlist.separator" combine="interleave">
<optional>
<attribute>
<name ns="">help</name>
<text/>
</attribute>
</optional>
</define>
<define name="label">
<element>
<name ns="">label</name>
<ref name="attlist.label"/>
<empty/>
</element>
</define>
<define name="attlist.label" combine="interleave">
<optional>
<attribute a:defaultValue="">
<name ns="">string</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.label" combine="interleave">
<choice>
<attribute>
<name ns="">name</name>
<text/>
</attribute>
<attribute>
<name ns="">name</name>
<text/>
</attribute>
</choice>
</define>
<define name="attlist.label" combine="interleave">
<optional>
<attribute>
<name ns="">states</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.label" combine="interleave">
<optional>
<attribute a:defaultValue="0.0">
<name ns="">xalign</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.label" combine="interleave">
<optional>
<attribute a:defaultValue="0.5">
<name ns="">yalign</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.label" combine="interleave">
<optional>
<attribute a:defaultValue="0">
<name ns="">yexpand</name>
<choice>
<value>0</value>
<value>1</value>
</choice>
</attribute>
</optional>
</define>
<define name="attlist.label" combine="interleave">
<optional>
<attribute a:defaultValue="0">
<name ns="">yfill</name>
<choice>
<value>0</value>
<value>1</value>
</choice>
</attribute>
</optional>
</define>
<define name="attlist.label" combine="interleave">
<optional>
<attribute a:defaultValue="0">
<name ns="">xexpand</name>
<choice>
<value>0</value>
<value>1</value>
</choice>
</attribute>
</optional>
</define>
<define name="attlist.label" combine="interleave">
<optional>
<attribute a:defaultValue="1">
<name ns="">xfill</name>
<choice>
<value>0</value>
<value>1</value>
</choice>
</attribute>
</optional>
</define>
<define name="attlist.label" combine="interleave">
<optional>
<attribute a:defaultValue="1">
<name ns="">colspan</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.label" combine="interleave">
<optional>
<attribute>
<name ns="">help</name>
<text/>
</attribute>
</optional>
</define>
<define name="newline">
<element>
<name ns="">newline</name>
<ref name="attlist.newline"/>
<empty/>
</element>
</define>
<define name="attlist.newline" combine="interleave">
<attribute>
<name ns="">id</name>
<text/>
</attribute>
</define>
<define name="link">
<element>
<name ns="">link</name>
<ref name="attlist.link"/>
<empty/>
</element>
</define>
<define name="attlist.link" combine="interleave">
<attribute>
<name ns="">name</name>
<text/>
</attribute>
</define>
<define name="attlist.link" combine="interleave">
<optional>
<attribute>
<name ns="">id</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.link" combine="interleave">
<optional>
<attribute a:defaultValue="1">
<name ns="">colspan</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.link" combine="interleave">
<optional>
<attribute>
<name ns="">states</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.link" combine="interleave">
<optional>
<attribute>
<name ns="">icon</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.link" combine="interleave">
<optional>
<attribute a:defaultValue="show">
<name ns="">empty</name>
<choice>
<value>show</value>
<value>hide</value>
</choice>
</attribute>
</optional>
</define>
<define name="notebook">
<element>
<name ns="">notebook</name>
<ref name="attlist.notebook"/>
<zeroOrMore>
<ref name="page"/>
</zeroOrMore>
</element>
</define>
<define name="attlist.notebook" combine="interleave">
<optional>
<attribute a:defaultValue="4">
<name ns="">colspan</name>
<text/>
</attribute>
</optional>
</define>
<define name="page">
<element>
<name ns="">page</name>
<ref name="attlist.page"/>
<zeroOrMore>
<choice>
<ref name="image"/>
<ref name="separator"/>
<ref name="label"/>
<ref name="newline"/>
<ref name="link"/>
<ref name="notebook"/>
<ref name="group"/>
<ref name="hpaned"/>
<ref name="vpaned"/>
<ref name="action"/>
</choice>
</zeroOrMore>
</element>
</define>
<define name="attlist.page" combine="interleave">
<optional>
<attribute>
<name ns="">angle</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.page" combine="interleave">
<optional>
<attribute a:defaultValue="Unknown">
<name ns="">string</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.page" combine="interleave">
<optional>
<attribute a:defaultValue="4">
<name ns="">col</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.page" combine="interleave">
<choice>
<attribute>
<name ns="">name</name>
<text/>
</attribute>
<attribute>
<name ns="">id</name>
<text/>
</attribute>
</choice>
</define>
<define name="group">
<element>
<name ns="">group</name>
<ref name="attlist.group"/>
<zeroOrMore>
<choice>
<ref name="image"/>
<ref name="separator"/>
<ref name="label"/>
<ref name="newline"/>
<ref name="link"/>
<ref name="notebook"/>
<ref name="group"/>
<ref name="hpaned"/>
<ref name="vpaned"/>
<ref name="action"/>
</choice>
</zeroOrMore>
</element>
</define>
<define name="attlist.group" combine="interleave">
<optional>
<attribute>
<name ns="">string</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.group" combine="interleave">
<optional>
<attribute a:defaultValue="1">
<name ns="">colspan</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.group" combine="interleave">
<optional>
<attribute a:defaultValue="0">
<name ns="">yexpand</name>
<choice>
<value>0</value>
<value>1</value>
</choice>
</attribute>
</optional>
</define>
<define name="attlist.group" combine="interleave">
<optional>
<attribute a:defaultValue="1">
<name ns="">yfill</name>
<choice>
<value>0</value>
<value>1</value>
</choice>
</attribute>
</optional>
</define>
<define name="attlist.group" combine="interleave">
<optional>
<attribute a:defaultValue="0.5">
<name ns="">yalign</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.group" combine="interleave">
<optional>
<attribute a:defaultValue="0">
<name ns="">xexpand</name>
<choice>
<value>0</value>
<value>1</value>
</choice>
</attribute>
</optional>
</define>
<define name="attlist.group" combine="interleave">
<optional>
<attribute a:defaultValue="1">
<name ns="">xfill</name>
<choice>
<value>0</value>
<value>1</value>
</choice>
</attribute>
</optional>
</define>
<define name="attlist.group" combine="interleave">
<optional>
<attribute a:defaultValue="0.5">
<name ns="">xalign</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.group" combine="interleave">
<optional>
<attribute a:defaultValue="1">
<name ns="">rowspan</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.group" combine="interleave">
<optional>
<attribute a:defaultValue="4">
<name ns="">col</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.group" combine="interleave">
<attribute>
<name ns="">id</name>
<text/>
</attribute>
</define>
<define name="hpaned">
<element>
<name ns="">hpaned</name>
<ref name="attlist.paned"/>
<zeroOrMore>
<ref name="child"/>
</zeroOrMore>
</element>
</define>
<define name="vpaned">
<element>
<name ns="">vpaned</name>
<ref name="attlist.paned"/>
<zeroOrMore>
<ref name="child"/>
</zeroOrMore>
</element>
</define>
<define name="attlist.paned" combine="interleave">
<optional>
<attribute a:defaultValue="4">
<name ns="">colspan</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.paned" combine="interleave">
<optional>
<attribute>
<name ns="">position</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.paned" combine="interleave">
<attribute>
<name ns="">id</name>
<text/>
</attribute>
</define>
<define name="child">
<element>
<name ns="">child</name>
<ref name="attlist.child"/>
<zeroOrMore>
<choice>
<ref name="image"/>
<ref name="separator"/>
<ref name="label"/>
<ref name="newline"/>
<ref name="link"/>
<ref name="notebook"/>
<ref name="group"/>
<ref name="hpaned"/>
<ref name="vpaned"/>
<ref name="action"/>
</choice>
</zeroOrMore>
</element>
</define>
<define name="attlist.child" combine="interleave">
<attribute>
<name ns="">id</name>
<text/>
</attribute>
</define>
<define name="data">
<element>
<name ns="">data</name>
<ref name="attlist.data"/>
<oneOrMore>
<ref name="xpath"/>
</oneOrMore>
</element>
</define>
<define name="attlist.data" combine="interleave">
<empty/>
</define>
<define name="xpath">
<element>
<name ns="">xpath</name>
<ref name="attlist.xpath"/>
<zeroOrMore>
<choice>
<ref name="image"/>
<ref name="separator"/>
<ref name="label"/>
<ref name="newline"/>
<ref name="link"/>
<ref name="notebook"/>
<ref name="group"/>
<ref name="hpaned"/>
<ref name="vpaned"/>
<ref name="action"/>
<ref name="page"/>
<ref name="child"/>
<ref name="board"/>
</choice>
</zeroOrMore>
</element>
</define>
<define name="attlist.xpath" combine="interleave">
<attribute>
<name ns="">expr</name>
<text/>
</attribute>
</define>
<define name="attlist.xpath" combine="interleave">
<optional>
<attribute a:defaultValue="inside">
<name ns="">position</name>
<choice>
<value>inside</value>
<value>replace</value>
<value>replace_attributes</value>
<value>after</value>
<value>before</value>
</choice>
</attribute>
</optional>
</define>
<define name="action">
<element>
<name ns="">action</name>
<ref name="attlist.action"/>
<empty/>
</element>
</define>
<define name="attlist.action" combine="interleave">
<attribute>
<name ns="">name</name>
<text/>
</attribute>
</define>
<define name="attlist.action" combine="interleave">
<optional>
<attribute a:defaultValue="1">
<name ns="">colspan</name>
<text/>
</attribute>
</optional>
</define>
<define name="label" combine="choice">
<notAllowed/>
</define>
<start>
<choice>
<ref name="data"/>
<ref name="board"/>
<ref name="label"/>
</choice>
</start>
</grammar>

36
ir/ui/calendar.rnc Executable file
View File

@@ -0,0 +1,36 @@
namespace a = "http://relaxng.org/ns/compatibility/annotations/1.0"
calendar = element calendar { attlist.calendar, field* }
attlist.calendar &=
attribute dtstart { text }
attlist.calendar &=
attribute dtend { text }?
attlist.calendar &=
attribute mode { "day"
| "week"
| "month" }?
attlist.calendar &= [a:defaultValue = "1" ] attribute editable { "0" | "1" }?
attlist.calendar &= attribute color { text }?
attlist.calendar &= attribute background_color { text }?
attlist.calendar &=
attribute width { text }?
attlist.calendar &=
attribute height { text }?
field = element field { attlist.field, empty }
attlist.field &= attribute name { text }
data = element data { attlist.data, xpath* }
attlist.data &= empty
xpath = element xpath {
attlist.xpath, (field | calendar)*
}
attlist.xpath &= attribute expr { text }
attlist.xpath &=
[ a:defaultValue = "inside" ]
attribute position { "inside"
| "replace"
| "replace_attributes"
| "after"
| "before" }?
start = data | calendar

145
ir/ui/calendar.rng Executable file
View File

@@ -0,0 +1,145 @@
<?xml version="1.0" encoding="UTF-8"?>
<grammar xmlns="http://relaxng.org/ns/structure/1.0"
xmlns:a="http://relaxng.org/ns/compatibility/annotations/1.0">
<define name="calendar">
<element>
<name ns="">calendar</name>
<ref name="attlist.calendar"/>
<zeroOrMore>
<ref name="field"/>
</zeroOrMore>
</element>
</define>
<define name="attlist.calendar" combine="interleave">
<attribute>
<name ns="">dtstart</name>
<text/>
</attribute>
</define>
<define name="attlist.calendar" combine="interleave">
<optional>
<attribute>
<name ns="">dtend</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.calendar" combine="interleave">
<optional>
<attribute>
<name ns="">mode</name>
<choice>
<value>day</value>
<value>week</value>
<value>month</value>
</choice>
</attribute>
</optional>
</define>
<define name="attlist.calendar" combine="interleave">
<optional>
<attribute a:defaultValue="1">
<name ns="">editable</name>
<choice>
<value>0</value>
<value>1</value>
</choice>
</attribute>
</optional>
</define>
<define name="attlist.calendar" combine="interleave">
<optional>
<attribute>
<name ns="">color</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.calendar" combine="interleave">
<optional>
<attribute>
<name ns="">background_color</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.calendar" combine="interleave">
<optional>
<attribute>
<name ns="">width</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.calendar" combine="interleave">
<optional>
<attribute>
<name ns="">height</name>
<text/>
</attribute>
</optional>
</define>
<define name="field">
<element>
<name ns="">field</name>
<ref name="attlist.field"/>
<empty/>
</element>
</define>
<define name="attlist.field" combine="interleave">
<attribute>
<name ns="">name</name>
<text/>
</attribute>
</define>
<define name="data">
<element>
<name ns="">data</name>
<ref name="attlist.data"/>
<zeroOrMore>
<ref name="xpath"/>
</zeroOrMore>
</element>
</define>
<define name="attlist.data" combine="interleave">
<empty/>
</define>
<define name="xpath">
<element>
<name ns="">xpath</name>
<ref name="attlist.xpath"/>
<zeroOrMore>
<choice>
<ref name="field"/>
<ref name="calendar"/>
</choice>
</zeroOrMore>
</element>
</define>
<define name="attlist.xpath" combine="interleave">
<attribute>
<name ns="">expr</name>
<text/>
</attribute>
</define>
<define name="attlist.xpath" combine="interleave">
<optional>
<attribute a:defaultValue="inside">
<name ns="">position</name>
<choice>
<value>inside</value>
<value>replace</value>
<value>replace_attributes</value>
<value>after</value>
<value>before</value>
</choice>
</attribute>
</optional>
</define>
<start>
<choice>
<ref name="data"/>
<ref name="calendar"/>
</choice>
</start>
</grammar>

300
ir/ui/form.rnc Executable file
View File

@@ -0,0 +1,300 @@
namespace a = "http://relaxng.org/ns/compatibility/annotations/1.0"
form =
element form {
attlist.form,
(label
| field
| image
| separator
| newline
| button
| link
| notebook
| group
| hpaned
| vpaned)*
}
attlist.form &= attribute on_write { text }?
attlist.form &=
[ a:defaultValue = "1" ] attribute creatable { "0" | "1" }?
attlist.form &= [ a:defaultValue = "4" ] attribute col { text }?
attlist.form &= attribute cursor { text }?
attlist.form &= attribute scan_code { "one" | "submit" | "loop" }?
attlist.form &= attribute scan_code_depends { text }?
attlist.form &= attribute scan_code_states { text }?
label = element label { attlist.label, empty }
attlist.label &= [ a:defaultValue = "" ] attribute string { text }?
attlist.label &= ( attribute name { text } | attribute id { text } )
attlist.label &= attribute states { text }?
attlist.label &= [ a:defaultValue = "0.0" ] attribute xalign { text }?
attlist.label &= [ a:defaultValue = "0.5" ] attribute yalign { text }?
attlist.label &=
[ a:defaultValue = "0" ] attribute yexpand { "0" | "1" }?
attlist.label &= [ a:defaultValue = "0" ] attribute yfill { "0" | "1" }?
attlist.label &=
[ a:defaultValue = "0" ] attribute xexpand { "0" | "1" }?
attlist.label &= [ a:defaultValue = "1" ] attribute xfill { "0" | "1" }?
attlist.label &= [ a:defaultValue = "1" ] attribute colspan { text }?
attlist.label &= attribute help { text }?
field = element field { attlist.field, empty }
attlist.field &= attribute name { text }
attlist.field &= [ a:defaultValue = "1" ] attribute colspan { text }?
attlist.field &=
attribute widget {
"binary"
| "boolean"
| "callto"
| "char"
| "date"
| "datetime"
| "dict"
| "document"
| "email"
| "float"
| "html"
| "image"
| "integer"
| "many2many"
| "many2one"
| "multiselection"
| "numeric"
| "one2many"
| "one2one"
| "password"
| "progressbar"
| "pyson"
| "reference"
| "richtext"
| "selection"
| "sip"
| "text"
| "time"
| "timedelta"
| "url"
}?
attlist.field &= attribute fill { "0" | "1" }?
attlist.field &= attribute yexpand { "0" | "1" }?
attlist.field &= attribute yfill { "0" | "1" }?
attlist.field &= attribute empty { "0" | "1" }?
attlist.field &=
[ a:defaultValue = "1" ] attribute xexpand { "0" | "1" }?
attlist.field &= [ a:defaultValue = "1" ] attribute xfill { "0" | "1" }?
attlist.field &= [ a:defaultValue = "0.0" ] attribute xalign { text }?
attlist.field &= [ a:defaultValue = "0.5" ] attribute yalign { text }?
attlist.field &= attribute help { text }?
attlist.field &= attribute width { text }?
attlist.field &= attribute height { text }?
attlist.field &= attribute readonly { "0" | "1" }?
attlist.field &=
[ a:defaultValue = "0" ] attribute tree_invisible { "0" | "1" }?
attlist.field &= attribute mode { text }?
attlist.field &= attribute view_ids { text }?
attlist.field &= attribute product { text }?
attlist.field &=
[ a:defaultValue = "0" ] attribute invisible { "0" | "1" }?
attlist.field &=
[ a:defaultValue = "1" ] attribute create { "0" | "1" }?
attlist.field &=
[ a:defaultValue = "1" ] attribute delete { "0" | "1" }?
attlist.field &=
[ a:defaultValue = "left_to_right" ] attribute orientation {
"left_to_right"
| "right_to_left"
| "bottom_to_top"
| "top_to_bottom"
}?
attlist.field &= attribute spell { text }?
attlist.field &=
[a:defaultValue = "0"] attribute filename_visible { "0" | "1" }?
attlist.field &= [a:defaultValue = "0"] attribute pre_validate { "0" | "1" }?
attlist.field &= attribute icon { text }?
attlist.field &= [a:defaultValue = "1"] attribute completion { "0" | "1" }?
attlist.field &= attribute string { text }?
attlist.field &= [a:defaultValue = "1"] attribute factor { text }?
attlist.field &= attribute filename { text }?
attlist.field &= attribute help_field { text }?
attlist.field &=
[a:defaultValue = "0"] attribute toolbar { "0" | "1" }?
attlist.field &= attribute symbol { text }?
attlist.field &= [a:defaultValue = "1"] attribute grouping { "0" | "1" }?
attlist.field &= [a:defaultValue = "square"] attribute border { "square" | "circle" | "rounded" }?
attlist.field &= attribute loading { "lazy" | "eager" }?
image = element image { attlist.image, empty }
attlist.image &= attribute name { text }
attlist.image &= [a:defaultValue = "icon"] attribute type { "icon" | "url" }?
attlist.image &= attribute url_size { text }?
attlist.image &= [a:defaultValue = "square"] attribute border { "square" | "circle" | "rounded" }?
attlist.image &= [ a:defaultValue = "1" ] attribute colspan { text }?
attlist.image &=
[ a:defaultValue = "0" ] attribute yexpand { "0" | "1" }?
attlist.image &= [ a:defaultValue = "0" ] attribute yfill { "0" | "1" }?
attlist.image &=
[ a:defaultValue = "1" ] attribute xexpand { "0" | "1" }?
attlist.image &=
[ a:defaultValue = "48" ] attribute size {text }?
attlist.image &= [ a:defaultValue = "0" ] attribute xfill { "0" | "1" }?
attlist.image &= attribute help { text }?
attlist.image &= attribute states { text }?
separator = element separator { attlist.separator, empty }
attlist.separator &= [ a:defaultValue = "" ] attribute string { text }?
attlist.separator &= ( attribute name { text } | attribute id { text} )
attlist.separator &= attribute states { text }?
attlist.separator &=
[ a:defaultValue = "0.0" ] attribute xalign { text }?
attlist.separator &=
[ a:defaultValue = "1" ] attribute colspan { text }?
attlist.separator &=
[ a:defaultValue = "0" ] attribute yexpand { "0" | "1" }?
attlist.separator &=
[ a:defaultValue = "0" ] attribute yfill { "0" | "1" }?
attlist.separator &=
[ a:defaultValue = "1" ] attribute xexpand { "0" | "1" }?
attlist.separator &=
[ a:defaultValue = "0" ] attribute xfill { "0" | "1" }?
attlist.separator &= attribute help { text }?
newline = element newline { attlist.newline, empty }
attlist.newline &= empty
button = element button { attlist.button, empty }
attlist.button &= [ a:defaultValue = "1" ] attribute colspan { text }?
attlist.button &= attribute help { text }?
attlist.button &= attribute string { text }?
attlist.button &= attribute icon { text }?
attlist.button &= attribute confirm { text }?
attlist.button &= attribute name { text }
attlist.button &= attribute states { text }?
attlist.button &=
[ a:defaultValue = "0" ] attribute yexpand { "0" | "1" }?
attlist.button &=
[ a:defaultValue = "0" ] attribute yfill { "0" | "1" }?
attlist.button &=
[ a:defaultValue = "1" ] attribute xexpand { "0" | "1" }?
attlist.button &=
[ a:defaultValue = "0" ] attribute xfill { "0" | "1" }?
attlist.button &=
[ a:defaultValue="action" ]
attribute keyword { "relate" | "print" | "action" }?
attlist.button &= [ a:defaultValue = "0" ] attribute rule { "0" | "1" }?
attlist.button &= attribute change { text }?
attlist.button &= attribute type { "class" | "instance" }?
link = element link { attlist.link, empty }
attlist.link &= attribute name { text }
attlist.link &= attribute id { text }?
attlist.link &= [ a:defaultValue = "1" ] attribute colspan { text }?
attlist.link &= attribute states { text }?
attlist.link &= attribute icon { text }?
attlist.link &= [ a:defaultValue = "show" ] attribute empty { "show" | "hide" }?
notebook = element notebook { attlist.notebook, page* }
attlist.notebook &= [ a:defaultValue = "4" ] attribute colspan { text }?
attlist.notebook &= [ a:defaultValue = "1" ] attribute yexpand { "0" | "1" }?
attlist.notebook &= [ a:defaultValue = "1" ] attribute yfill { "0" | "1" }?
attlist.notebook &= attribute states { text }?
attlist.notebook &= attribute width { text }?
attlist.notebook &= attribute height { text }?
page =
element page {
attlist.page,
(label
| field
| image
| separator
| newline
| button
| link
| notebook
| group
| hpaned
| vpaned)*
}
attlist.page &= attribute angle { text }?
attlist.page &= attribute icon { text }?
attlist.page &=
[ a:defaultValue = "Unknown" ] attribute string { text }?
attlist.page &= [ a:defaultValue = "4" ] attribute col { text }?
attlist.page &= ( attribute name { text } | attribute id { text } )
attlist.page &= attribute states { text }?
group =
element group {
attlist.group,
(label
| field
| image
| separator
| newline
| button
| link
| notebook
| group
| hpaned
| vpaned)*
}
attlist.group &= attribute string { text }?
attlist.group &= [ a:defaultValue = "1" ] attribute colspan { text }?
attlist.group &=
[ a:defaultValue = "0" ] attribute yexpand { "0" | "1" }?
attlist.group &= [ a:defaultValue = "1" ] attribute yfill { "0" | "1" }?
attlist.group &= [ a:defaultValue = "0.5" ] attribute yalign { text }?
attlist.group &=
[ a:defaultValue = "0" ] attribute xexpand { "0" | "1" }?
attlist.group &= [ a:defaultValue = "1" ] attribute xfill { "0" | "1" }?
attlist.group &= [ a:defaultValue = "0.5" ] attribute xalign { text }?
attlist.group &= [ a:defaultValue = "1" ] attribute rowspan { text }?
attlist.group &= [ a:defaultValue = "4" ] attribute col { text }?
attlist.group &= ( attribute name { text } | attribute id { text } )
attlist.group &= attribute states { text }?
attlist.group &= [ a:defaultValue = "0" ] attribute homogeneous { "0" | "1" }?
attlist.group &= attribute expandable { "0" | "1" }?
hpaned = element hpaned { attlist.paned, child* }
vpaned = element vpaned { attlist.paned, child* }
attlist.paned &= [ a:defaultValue = "4" ] attribute colspan { text }?
attlist.paned &= attribute position { text }?
attlist.paned &= attribute id { text }
child =
element child {
attlist.child,
(label
| field
| image
| separator
| newline
| button
| link
| notebook
| group
| hpaned
| vpaned)*
}
attlist.child &= attribute id { text }
data = element data { attlist.data, xpath* }
attlist.data &= empty
xpath = element xpath { attlist.xpath,
(label
| field
| image
| separator
| newline
| button
| link
| notebook
| group
| hpaned
| vpaned
| form
| page
| child)*
}
attlist.xpath &= attribute expr { text }
attlist.xpath &=
[ a:defaultValue = "inside" ]
attribute position { "inside" | "replace" | "replace_attributes" | "after" | "before" }?
start = data | form

1447
ir/ui/form.rng Executable file

File diff suppressed because it is too large Load Diff

39
ir/ui/graph.rnc Executable file
View File

@@ -0,0 +1,39 @@
namespace a = "http://relaxng.org/ns/compatibility/annotations/1.0"
graph = element graph { attlist.graph, x*, y* }
attlist.graph &=
[ a:defaultValue = "vbar" ]
attribute type { "vbar" | "hbar" | "line" | "pie" }?
attlist.graph &= attribute background { text }?
attlist.graph &= attribute color { text }?
attlist.graph &=
[ a:defaultValue = "1" ] attribute legend { "0" | "1" }?
x = element x { attlist.x, field }
attlist.x &= empty
y = element y { attlist.y, field+ }
attlist.y &= empty
field = element field { attlist.field, empty }
attlist.field &= attribute name { text }
attlist.field &= attribute string { text }?
attlist.field &= attribute key { text }?
attlist.field &= attribute domain { text }?
attlist.field &= attribute fill { "0" | "1" }?
attlist.field &= attribute empty { "0" | "1" }?
attlist.field &= attribute color { text }?
attlist.field &= attribute timedelta { text }?
attlist.field &=
[ a:defaultValue = "linear" ]
attribute interpolation { "constant-left" | "constant-right" | "constant-center" | "linear" }?
data = element data { attlist.data, xpath* }
attlist.data &= empty
xpath = element xpath { attlist.xpath,
(graph
| x
| y
| field)*
}
attlist.xpath &= attribute expr { text }
attlist.xpath &=
[ a:defaultValue = "inside" ]
attribute position { "inside" | "replace" | "replace_attributes" | "after" | "before" }?
start = data | graph

218
ir/ui/graph.rng Executable file
View File

@@ -0,0 +1,218 @@
<?xml version="1.0" encoding="UTF-8"?>
<grammar xmlns="http://relaxng.org/ns/structure/1.0"
xmlns:a="http://relaxng.org/ns/compatibility/annotations/1.0">
<define name="graph">
<element>
<name ns="">graph</name>
<ref name="attlist.graph"/>
<zeroOrMore>
<ref name="x"/>
</zeroOrMore>
<zeroOrMore>
<ref name="y"/>
</zeroOrMore>
</element>
</define>
<define name="attlist.graph" combine="interleave">
<optional>
<attribute a:defaultValue="vbar">
<name ns="">type</name>
<choice>
<value>vbar</value>
<value>hbar</value>
<value>line</value>
<value>pie</value>
</choice>
</attribute>
</optional>
</define>
<define name="attlist.graph" combine="interleave">
<optional>
<attribute>
<name ns="">background</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.graph" combine="interleave">
<optional>
<attribute>
<name ns="">color</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.graph" combine="interleave">
<optional>
<attribute a:defaultValue="1">
<name ns="">legend</name>
<choice>
<value>0</value>
<value>1</value>
</choice>
</attribute>
</optional>
</define>
<define name="x">
<element>
<name ns="">x</name>
<ref name="attlist.x"/>
<ref name="field"/>
</element>
</define>
<define name="attlist.x" combine="interleave">
<empty/>
</define>
<define name="y">
<element>
<name ns="">y</name>
<ref name="attlist.y"/>
<oneOrMore>
<ref name="field"/>
</oneOrMore>
</element>
</define>
<define name="attlist.y" combine="interleave">
<empty/>
</define>
<define name="field">
<element>
<name ns="">field</name>
<ref name="attlist.field"/>
<empty/>
</element>
</define>
<define name="attlist.field" combine="interleave">
<attribute>
<name ns="">name</name>
<text/>
</attribute>
</define>
<define name="attlist.field" combine="interleave">
<optional>
<attribute>
<name ns="">string</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.field" combine="interleave">
<optional>
<attribute>
<name ns="">key</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.field" combine="interleave">
<optional>
<attribute>
<name ns="">domain</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.field" combine="interleave">
<optional>
<attribute>
<name ns="">fill</name>
<choice>
<value>0</value>
<value>1</value>
</choice>
</attribute>
</optional>
</define>
<define name="attlist.field" combine="interleave">
<optional>
<attribute>
<name ns="">empty</name>
<choice>
<value>0</value>
<value>1</value>
</choice>
</attribute>
</optional>
</define>
<define name="attlist.field" combine="interleave">
<optional>
<attribute>
<name ns="">color</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.field" combine="interleave">
<optional>
<attribute>
<name ns="">timedelta</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.field" combine="interleave">
<optional>
<attribute a:defaultValue="linear">
<name ns="">interpolation</name>
<choice>
<value>constant-left</value>
<value>constant-right</value>
<value>constant-center</value>
<value>linear</value>
</choice>
</attribute>
</optional>
</define>
<define name="data">
<element>
<name ns="">data</name>
<ref name="attlist.data"/>
<zeroOrMore>
<ref name="xpath"/>
</zeroOrMore>
</element>
</define>
<define name="attlist.data" combine="interleave">
<empty/>
</define>
<define name="xpath">
<element>
<name ns="">xpath</name>
<ref name="attlist.xpath"/>
<zeroOrMore>
<choice>
<ref name="graph"/>
<ref name="x"/>
<ref name="y"/>
<ref name="field"/>
</choice>
</zeroOrMore>
</element>
</define>
<define name="attlist.xpath" combine="interleave">
<attribute>
<name ns="">expr</name>
<text/>
</attribute>
</define>
<define name="attlist.xpath" combine="interleave">
<optional>
<attribute a:defaultValue="inside">
<name ns="">position</name>
<choice>
<value>inside</value>
<value>replace</value>
<value>replace_attributes</value>
<value>after</value>
<value>before</value>
</choice>
</attribute>
</optional>
</define>
<start>
<choice>
<ref name="data"/>
<ref name="graph"/>
</choice>
</start>
</grammar>

87
ir/ui/icon.py Executable file
View File

@@ -0,0 +1,87 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of this
# repository contains the full copyright notices and license terms.
import os
from trytond.cache import Cache
from trytond.model import ModelSQL, ModelView, fields, sequence_ordered
from trytond.rpc import RPC
from trytond.tools import file_open
from trytond.transaction import Transaction
class Icon(
fields.fmany2one(
'module_ref', 'module', 'ir.module,name', "Module",
readonly=True, required=True, ondelete='CASCADE'),
sequence_ordered(), ModelSQL, ModelView):
'Icon'
__name__ = 'ir.ui.icon'
name = fields.Char('Name', required=True)
module = fields.Char('Module', readonly=True, required=True)
path = fields.Char('SVG Path', readonly=True, required=True)
icon = fields.Function(fields.Char('Icon', depends=['path']), 'get_icon')
_list_icons = Cache('ir.ui.icon.list_icons', context=False)
@classmethod
def __setup__(cls):
super(Icon, cls).__setup__()
cls.__rpc__.update({
'list_icons': RPC(),
})
@classmethod
def __register__(cls, module_name):
super().__register__(module_name)
table = cls.__table_handler__(module_name)
# Migration from 5.0: remove required on sequence
table.not_null_action('sequence', 'remove')
@staticmethod
def default_module():
return Transaction().context.get('module') or ''
@staticmethod
def default_sequence():
return 10
@classmethod
def list_icons(cls):
icons = cls._list_icons.get(None)
if icons is not None:
return icons
icons = {}
for icon in cls.browse(cls.search([],
order=[('sequence', 'ASC'), ('id', 'ASC')])):
if icon.name not in icons:
icons[icon.name] = icon.id
icons = sorted((icon_id, name) for name, icon_id in icons.items())
cls._list_icons.set(None, icons)
return icons
def get_icon(self, name):
path = os.path.join(self.module, self.path.replace('/', os.sep))
with file_open(
path,
subdir='modules' if self.module not in {'ir', 'res'} else '',
mode='r', encoding='utf-8') as fp:
return fp.read()
@classmethod
def create(cls, vlist):
icons = super().create(vlist)
cls._list_icons.clear()
return icons
@classmethod
def write(cls, *args):
super().write(*args)
cls._list_icons.clear()
@classmethod
def delete(cls, icons):
super().delete(icons)
cls._list_icons.clear()

38
ir/ui/icon.xml Executable file
View File

@@ -0,0 +1,38 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<tryton>
<data>
<record model="ir.ui.view" id="icon_view_tree">
<field name="model">ir.ui.icon</field>
<field name="type">tree</field>
<field name="name">icon_view_list</field>
</record>
<record model="ir.ui.view" id="icon_view_form">
<field name="model">ir.ui.icon</field>
<field name="type">form</field>
<field name="name">icon_view_form</field>
</record>
<record model="ir.action.act_window" id="act_icon_form">
<field name="name">Icons</field>
<field name="res_model">ir.ui.icon</field>
</record>
<record model="ir.action.act_window.view"
id="act_icon_form_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="icon_view_tree"/>
<field name="act_window" ref="act_icon_form"/>
</record>
<record model="ir.action.act_window.view"
id="act_icon_form_view2">
<field name="sequence" eval="20"/>
<field name="view" ref="icon_view_form"/>
<field name="act_window" ref="act_icon_form"/>
</record>
<menuitem
parent="menu_ui"
action="act_icon_form"
sequence="20"
id="menu_icon_form"/>
</data>
</tryton>

4
ir/ui/icons/tryton-board.svg Executable file
View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path d="M0 0h24v24H0z" fill="none"/>
<path d="M3 13h8V3H3v10zm0 8h8v-6H3v6zm10 0h8V11h-8v10zm0-18v6h8V3h-8z"/>
</svg>

After

Width:  |  Height:  |  Size: 211 B

View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path d="M9 11H7v2h2v-2zm4 0h-2v2h2v-2zm4 0h-2v2h2v-2zm2-7h-1V2h-2v2H8V2H6v2H5c-1.11 0-1.99.9-1.99 2L3 20c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 16H5V9h14v11z"/>
<path fill="none" d="M0 0h24v24H0z"/>
</svg>

After

Width:  |  Height:  |  Size: 313 B

4
ir/ui/icons/tryton-folder.svg Executable file
View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path d="M10 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z"/>
<path d="M0 0h24v24H0z" fill="none"/>
</svg>

After

Width:  |  Height:  |  Size: 241 B

4
ir/ui/icons/tryton-form.svg Executable file
View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"/>
<path d="M0 0h24v24H0z" fill="none"/>
</svg>

After

Width:  |  Height:  |  Size: 299 B

4
ir/ui/icons/tryton-graph.svg Executable file
View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path d="M9 17H7v-7h2v7zm4 0h-2V7h2v10zm4 0h-2v-4h2v4zm2.5 2.1h-15V5h15v14.1zm0-16.1h-15c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h15c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2z"/>
<path fill="none" d="M0 0h24v24H0z"/>
</svg>

After

Width:  |  Height:  |  Size: 298 B

4
ir/ui/icons/tryton-list.svg Executable file
View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path d="M4 15h16v-2H4v2zm0 4h16v-2H4v2zm0-8h16V9H4v2zm0-6v2h16V5H4z"/>
<path fill="none" d="M0 0h24v24H0V0z"/>
</svg>

After

Width:  |  Height:  |  Size: 211 B

View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
<path fill="none" d="M0 0h20v20H0V0z"/>
<path d="M15.95 10.78c.03-.25.05-.51.05-.78s-.02-.53-.06-.78l1.69-1.32c.15-.12.19-.34.1-.51l-1.6-2.77c-.1-.18-.31-.24-.49-.18l-1.99.8c-.42-.32-.86-.58-1.35-.78L12 2.34c-.03-.2-.2-.34-.4-.34H8.4c-.2 0-.36.14-.39.34l-.3 2.12c-.49.2-.94.47-1.35.78l-1.99-.8c-.18-.07-.39 0-.49.18l-1.6 2.77c-.1.18-.06.39.1.51l1.69 1.32c-.04.25-.07.52-.07.78s.02.53.06.78L2.37 12.1c-.15.12-.19.34-.1.51l1.6 2.77c.1.18.31.24.49.18l1.99-.8c.42.32.86.58 1.35.78l.3 2.12c.04.2.2.34.4.34h3.2c.2 0 .37-.14.39-.34l.3-2.12c.49-.2.94-.47 1.35-.78l1.99.8c.18.07.39 0 .49-.18l1.6-2.77c.1-.18.06-.39-.1-.51l-1.67-1.32zM10 13c-1.65 0-3-1.35-3-3s1.35-3 3-3 3 1.35 3 3-1.35 3-3 3z"/>
</svg>

After

Width:  |  Height:  |  Size: 786 B

4
ir/ui/icons/tryton-tree.svg Executable file
View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path d="M3 13h2v-2H3v2zm0 4h2v-2H3v2zm0-8h2V7H3v2zm4 4h14v-2H7v2zm0 4h14v-2H7v2zM7 7v2h14V7H7z"/>
<path d="M0 0h24v24H0z" fill="none"/>
</svg>

After

Width:  |  Height:  |  Size: 236 B

308
ir/ui/menu.py Executable file
View File

@@ -0,0 +1,308 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from collections import defaultdict
from itertools import groupby
from trytond.model import (
DeactivableMixin, ModelSQL, ModelView, fields, sequence_ordered, tree)
from trytond.pool import Pool
from trytond.rpc import RPC
from trytond.tools import grouped_slice
from trytond.transaction import Transaction, inactive_records
def one_in(i, j):
"""Check the presence of an element of setA in setB
"""
for k in i:
if k in j:
return True
return False
CLIENT_ICONS = [(x, x) for x in [
'tryton-add',
'tryton-archive',
'tryton-attach',
'tryton-back',
'tryton-barcode-scanner',
'tryton-bookmark-border',
'tryton-bookmark',
'tryton-bookmarks',
'tryton-cancel',
'tryton-clear',
'tryton-close',
'tryton-copy',
'tryton-create',
'tryton-date',
'tryton-delete',
'tryton-download',
'tryton-email',
'tryton-error',
'tryton-exit',
'tryton-export',
'tryton-filter',
'tryton-format-align-center',
'tryton-format-align-justify',
'tryton-format-align-left',
'tryton-format-align-right',
'tryton-format-bold',
'tryton-format-color-text',
'tryton-format-italic',
'tryton-format-underline',
'tryton-forward',
'tryton-history',
'tryton-import',
'tryton-info',
'tryton-launch',
'tryton-link',
'tryton-log',
'tryton-menu',
'tryton-note',
'tryton-ok',
'tryton-open',
'tryton-print',
'tryton-public',
'tryton-refresh',
'tryton-remove',
'tryton-save',
'tryton-search',
'tryton-sound-off',
'tryton-sound-on',
'tryton-star-border',
'tryton-star',
'tryton-switch',
'tryton-translate',
'tryton-unarchive',
'tryton-undo',
'tryton-warning',
]]
class UIMenu(
DeactivableMixin,
sequence_ordered(order='ASC NULLS LAST'),
tree(separator=' / '),
ModelSQL, ModelView):
"UI menu"
__name__ = 'ir.ui.menu'
name = fields.Char('Menu', required=True, translate=True)
childs = fields.One2Many('ir.ui.menu', 'parent', 'Children')
parent = fields.Many2One('ir.ui.menu', 'Parent Menu', ondelete='CASCADE')
complete_name = fields.Function(fields.Char('Complete Name'),
'get_rec_name', searcher='search_rec_name')
icon = fields.Selection('list_icons', 'Icon', translate=False)
action = fields.Function(fields.Reference('Action',
selection=[
('', ''),
('ir.action.report', 'ir.action.report'),
('ir.action.act_window', 'ir.action.act_window'),
('ir.action.wizard', 'ir.action.wizard'),
('ir.action.url', 'ir.action.url'),
], translate=False), 'get_action', setter='set_action')
action_keywords = fields.One2Many(
'ir.action.keyword', 'model', "Action Keywords",
filter=[
('keyword', '=', 'tree_open'),
])
favorite = fields.Function(fields.Boolean('Favorite'), 'get_favorite')
@classmethod
def order_complete_name(cls, tables):
return cls.name.convert_order('name', tables, cls)
@staticmethod
def default_icon():
return 'tryton-folder'
@classmethod
def default_sequence(cls):
return 50
@staticmethod
def list_icons():
pool = Pool()
Icon = pool.get('ir.ui.icon')
return sorted(CLIENT_ICONS
+ [(name, name) for _, name in Icon.list_icons()])
@classmethod
def search_global(cls, text):
# TODO improve search clause
for record in cls.search([
('rec_name', 'ilike', '%%%s%%' % text),
]):
if record.action_keywords:
yield record, record.rec_name, record.icon
@classmethod
def search(cls, domain, offset=0, limit=None, order=None, count=False,
query=False):
pool = Pool()
ModelAccess = pool.get('ir.model.access')
transaction = Transaction()
def has_action_access(menu):
if menu.action_keywords and all(
k.action.type == 'ir.action.act_window'
for k in menu.action_keywords):
for keyword in menu.action_keywords:
res_model = keyword.action.action.res_model
if not res_model:
break
if ModelAccess.check(res_model, raise_exception=False):
break
else:
return False
return True
menus = super(UIMenu, cls).search(domain, offset=offset, limit=limit,
order=order, count=False, query=query)
if query:
return menus
if transaction.check_access and menus:
menus = list(filter(has_action_access, menus))
parent_ids = {x.parent.id for x in menus if x.parent}
parents = set()
for sub_parent_ids in grouped_slice(parent_ids):
parents.update(cls.search([
('id', 'in', list(sub_parent_ids)),
]))
# Re-browse to avoid side-cache access
menus = cls.browse([x.id for x in menus
if (x.parent and x.parent in parents) or not x.parent])
if count:
return len(menus)
return menus
@classmethod
@inactive_records
def get_action(cls, menus, name):
pool = Pool()
actions = dict((m.id, None) for m in menus)
menus = cls.browse(menus)
action_keywords = sum((list(m.action_keywords) for m in menus), [])
def action_type(keyword):
return keyword.action.type
action_keywords.sort(key=action_type)
for type, action_keywords in groupby(action_keywords, key=action_type):
action_keywords = list(action_keywords)
action2keywords = defaultdict(list)
for action_keyword in action_keywords:
model = action_keyword.model
actions[model.id] = '%s,-1' % type
action2keywords[action_keyword.action.id].append(
action_keyword)
Action = pool.get(type)
factions = Action.search([
('action', 'in', list(action2keywords.keys())),
])
for action in factions:
for action_keyword in action2keywords[action.id]:
actions[action_keyword.model.id] = str(action)
return actions
@classmethod
def set_action(cls, menus, name, value):
pool = Pool()
ActionKeyword = pool.get('ir.action.keyword')
action_keywords = []
transaction = Transaction()
for i in range(0, len(menus), transaction.database.IN_MAX):
sub_menus = menus[i:i + transaction.database.IN_MAX]
action_keywords += ActionKeyword.search([
('keyword', '=', 'tree_open'),
('model', 'in', [str(menu) for menu in sub_menus]),
])
if action_keywords:
with Transaction().set_context(_timestamp=False):
ActionKeyword.delete(action_keywords)
if not value:
return
if isinstance(value, str):
action_type, action_id = value.split(',')
else:
action_type, action_id = value
if int(action_id) <= 0:
return
Action = pool.get(action_type)
action = Action(int(action_id))
to_create = []
for menu in menus:
with Transaction().set_context(_timestamp=False):
to_create.append({
'keyword': 'tree_open',
'model': str(menu),
'action': action.action.id,
})
if to_create:
ActionKeyword.create(to_create)
@classmethod
def get_favorite(cls, menus, name):
pool = Pool()
Favorite = pool.get('ir.ui.menu.favorite')
user = Transaction().user
favorites = Favorite.search([
('menu', 'in', [m.id for m in menus]),
('user', '=', user),
])
menu2favorite = {
m.id: False if m.action_keywords else None for m in menus}
menu2favorite.update(dict((f.menu.id, True) for f in favorites))
return menu2favorite
class UIMenuFavorite(sequence_ordered(), ModelSQL, ModelView):
"Menu Favorite"
__name__ = 'ir.ui.menu.favorite'
menu = fields.Many2One('ir.ui.menu', 'Menu', required=True,
ondelete='CASCADE')
user = fields.Many2One('res.user', 'User', required=True,
ondelete='CASCADE')
@classmethod
def __setup__(cls):
super(UIMenuFavorite, cls).__setup__()
cls.__rpc__.update({
'get': RPC(check_access=False),
'set': RPC(check_access=False, readonly=False),
'unset': RPC(check_access=False, readonly=False),
})
@staticmethod
def default_user():
return Transaction().user
@classmethod
def get(cls):
user = Transaction().user
favorites = cls.search([
('user', '=', user),
])
return [(f.menu.id, f.menu.rec_name, f.menu.icon) for f in favorites]
@classmethod
def set(cls, menu_id):
user = Transaction().user
cls.create([{
'menu': menu_id,
'user': user,
}])
@classmethod
def unset(cls, menu_id):
user = Transaction().user
favorites = cls.search([
('menu', '=', menu_id),
('user', '=', user),
])
cls.delete(favorites)

76
ir/ui/menu.xml Executable file
View File

@@ -0,0 +1,76 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<tryton>
<data>
<record model="ir.ui.view" id="menu_view_tree_tree">
<field name="model">ir.ui.menu</field>
<field name="type">tree</field>
<field name="field_childs">childs</field>
<field name="priority" eval="20"/>
<field name="name">ui_menu_tree</field>
</record>
<record model="ir.ui.view" id="menu_view_list">
<field name="model">ir.ui.menu</field>
<field name="type">tree</field>
<field name="priority" eval="10"/>
<field name="name">ui_menu_list</field>
</record>
<record model="ir.ui.view" id="menu_view_form">
<field name="model">ir.ui.menu</field>
<field name="type">form</field>
<field name="name">ui_menu_form</field>
</record>
<record model="ir.action.act_window" id="act_menu_tree">
<field name="name">Menu</field>
<field name="res_model">ir.ui.menu</field>
<field name="usage">menu</field>
<field name="domain" eval="[('parent', '=', None)]" pyson="1"/>
</record>
<record model="ir.action.act_window.view"
id="act_menu_tree_view1">
<field name="sequence" eval="1"/>
<field name="view" ref="menu_view_tree_tree"/>
<field name="act_window" ref="act_menu_tree"/>
</record>
<record model="ir.action.act_window" id="act_menu_list">
<field name="name">Menu</field>
<field name="res_model">ir.ui.menu</field>
</record>
<record model="ir.action.act_window.view"
id="act_menu_list_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="menu_view_list"/>
<field name="act_window" ref="act_menu_list"/>
</record>
<record model="ir.action.act_window.view"
id="act_menu_list_view2">
<field name="sequence" eval="20"/>
<field name="view" ref="menu_view_form"/>
<field name="act_window" ref="act_menu_list"/>
</record>
<menuitem
parent="menu_ui"
action="act_menu_list"
sequence="10"
id="menu_menu_list"/>
<record model="ir.ui.view" id="menu_favorite_view_list">
<field name="model">ir.ui.menu.favorite</field>
<field name="type">tree</field>
<field name="name">ui_menu_favorite_list</field>
</record>
<record model="ir.ui.view" id="menu_favorite_view_form">
<field name="model">ir.ui.menu.favorite</field>
<field name="type">form</field>
<field name="name">ui_menu_favorite_form</field>
</record>
</data>
</tryton>

114
ir/ui/tree.rnc Executable file
View File

@@ -0,0 +1,114 @@
namespace a = "http://relaxng.org/ns/compatibility/annotations/1.0"
tree = element tree { attlist.tree,
(field
| button)*
}
attlist.tree &= attribute on_write { text }?
attlist.tree &= attribute editable { "0" | "1" }?
attlist.tree &=
[ a:defaultValue = "1" ] attribute creatable { "0" | "1" }?
attlist.tree &= attribute sequence { text }?
attlist.tree &=
[ a:defaultValue = "0" ] attribute keyword_open { "0" | "1" }?
attlist.tree &=
[ a:defaultValue = "0" ] attribute tree_state { "0" | "1" }?
attlist.tree &= attribute visual { text }?
field = element field { attlist.field, (prefix | suffix)* }
attlist.field &= attribute name { text }
attlist.field &= attribute readonly { "0" | "1" }?
attlist.field &=
attribute widget {
"binary"
| "boolean"
| "callto"
| "char"
| "date"
| "datetime"
| "email"
| "float"
| "image"
| "integer"
| "many2many"
| "many2one"
| "multiselection"
| "numeric"
| "one2many"
| "one2one"
| "progressbar"
| "reference"
| "selection"
| "sip"
| "text"
| "time"
| "timedelta"
| "url"
}?
attlist.field &=
[ a:defaultValue = "0" ] attribute tree_invisible { text }?
attlist.field &= attribute optional { "0" | "1" }?
attlist.field &=
[ a:defaultValue = "0" ] attribute expand { xsd:integer }?
attlist.field &= attribute visual { text }?
attlist.field &= attribute icon { text }?
attlist.field &=
[ a:defaultValue = "0" ] attribute sum { "0" | "1" }?
attlist.field &= attribute width { text }?
attlist.field &= attribute height { text }?
attlist.field &=
[ a:defaultValue = "1" ] attribute create { "0" | "1" }?
attlist.field &=
[ a:defaultValue = "1" ] attribute delete { "0" | "1" }?
attlist.field &=
[ a:defaultValue = "left_to_right" ] attribute orientation {
"left_to_right"
| "right_to_left"
| "bottom_to_top"
| "top_to_bottom"
}?
attlist.field &= [a:defaultValue = "0"] attribute pre_validate { "0" | "1" }?
attlist.field &= [a:defaultValue = "1"] attribute completion { "0" | "1" }?
attlist.field &= attribute string { text }?
attlist.field &= [a:defaultValue = "1"] attribute factor { text }?
attlist.field &= attribute filename { text }?
attlist.field &= attribute help_field { text }?
attlist.field &= attribute view_ids { text }?
attlist.field &= attribute symbol { text }?
attlist.field &= [a:defaultValue = "1"] attribute grouping { "0" | "1" }?
prefix = element prefix { attlist.affix, empty }
suffix = element suffix { attlist.affix, empty }
attlist.affix &= attribute string { text }?
attlist.affix &= ( attribute name { text } | attribute id { text } )
attlist.affix &= attribute icon { text }?
attlist.affix &= [a:defaultValue = "icon"] attribute icon_type { "icon" | "url" }?
attlist.affix &= attribute url_size { text }?
attlist.affix &= [a:defaultValue = "square"] attribute border { "square" | "circle" | "rounded" }?
button = element button { attlist.button, empty }
attlist.button &= attribute help { text }?
attlist.button &= attribute string { text }?
attlist.button &= attribute confirm { text }?
attlist.button &= attribute name { text }
attlist.button &= attribute states { text }?
attlist.button &=
[ a:defaultValue="action" ]
attribute keyword { "relate" | "print" | "action" }?
attlist.button &= attribute change { text }?
attlist.button &= attribute type { "class" | "instance" }?
attlist.button &=
[ a:defaultValue = "0" ] attribute tree_invisible { text }?
attlist.button &= attribute width { text }?
data = element data { attlist.data, xpath* }
attlist.data &= empty
xpath = element xpath { attlist.xpath,
(field
| prefix
| suffix
| button
| tree
)*
}
attlist.xpath &= attribute expr { text }
attlist.xpath &=
[ a:defaultValue = "inside" ]
attribute position { "inside" | "replace" | "replace_attributes" | "after" | "before" }?
start = tree | data

551
ir/ui/tree.rng Executable file
View File

@@ -0,0 +1,551 @@
<?xml version="1.0" encoding="UTF-8"?>
<grammar xmlns="http://relaxng.org/ns/structure/1.0"
xmlns:a="http://relaxng.org/ns/compatibility/annotations/1.0"
datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
<define name="tree">
<element>
<name ns="">tree</name>
<ref name="attlist.tree"/>
<zeroOrMore>
<choice>
<ref name="field"/>
<ref name="button"/>
</choice>
</zeroOrMore>
</element>
</define>
<define name="attlist.tree" combine="interleave">
<optional>
<attribute>
<name ns="">on_write</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.tree" combine="interleave">
<optional>
<attribute>
<name ns="">editable</name>
<choice>
<value>0</value>
<value>1</value>
</choice>
</attribute>
</optional>
</define>
<define name="attlist.tree" combine="interleave">
<optional>
<attribute a:defaultValue="1">
<name ns="">creatable</name>
<choice>
<value>0</value>
<value>1</value>
</choice>
</attribute>
</optional>
</define>
<define name="attlist.tree" combine="interleave">
<optional>
<attribute>
<name ns="">sequence</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.tree" combine="interleave">
<optional>
<attribute a:defaultValue="0">
<name ns="">keyword_open</name>
<choice>
<value>0</value>
<value>1</value>
</choice>
</attribute>
</optional>
</define>
<define name="attlist.tree" combine="interleave">
<optional>
<attribute a:defaultValue="0">
<name ns="">tree_state</name>
<choice>
<value>0</value>
<value>1</value>
</choice>
</attribute>
</optional>
</define>
<define name="attlist.tree" combine="interleave">
<optional>
<attribute>
<name ns="">visual</name>
<text/>
</attribute>
</optional>
</define>
<define name="field">
<element>
<name ns="">field</name>
<ref name="attlist.field"/>
<zeroOrMore>
<choice>
<ref name="prefix"/>
<ref name="suffix"/>
</choice>
</zeroOrMore>
</element>
</define>
<define name="attlist.field" combine="interleave">
<attribute>
<name ns="">name</name>
<text/>
</attribute>
</define>
<define name="attlist.field" combine="interleave">
<optional>
<attribute>
<name ns="">readonly</name>
<choice>
<value>0</value>
<value>1</value>
</choice>
</attribute>
</optional>
</define>
<define name="attlist.field" combine="interleave">
<optional>
<attribute>
<name ns="">widget</name>
<choice>
<value>binary</value>
<value>boolean</value>
<value>callto</value>
<value>char</value>
<value>date</value>
<value>datetime</value>
<value>email</value>
<value>float</value>
<value>image</value>
<value>integer</value>
<value>many2many</value>
<value>many2one</value>
<value>multiselection</value>
<value>numeric</value>
<value>one2many</value>
<value>one2one</value>
<value>progressbar</value>
<value>reference</value>
<value>selection</value>
<value>sip</value>
<value>text</value>
<value>time</value>
<value>timedelta</value>
<value>url</value>
</choice>
</attribute>
</optional>
</define>
<define name="attlist.field" combine="interleave">
<optional>
<attribute a:defaultValue="0">
<name ns="">tree_invisible</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.field" combine="interleave">
<optional>
<attribute>
<name ns="">optional</name>
<choice>
<value>0</value>
<value>1</value>
</choice>
</attribute>
</optional>
</define>
<define name="attlist.field" combine="interleave">
<optional>
<attribute a:defaultValue="0">
<name ns="">expand</name>
<data type="integer"/>
</attribute>
</optional>
</define>
<define name="attlist.field" combine="interleave">
<optional>
<attribute>
<name ns="">visual</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.field" combine="interleave">
<optional>
<attribute>
<name ns="">icon</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.field" combine="interleave">
<optional>
<attribute a:defaultValue="0">
<name ns="">sum</name>
<choice>
<value>0</value>
<value>1</value>
</choice>
</attribute>
</optional>
</define>
<define name="attlist.field" combine="interleave">
<optional>
<attribute>
<name ns="">width</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.field" combine="interleave">
<optional>
<attribute>
<name ns="">height</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.field" combine="interleave">
<optional>
<attribute a:defaultValue="1">
<name ns="">create</name>
<choice>
<value>0</value>
<value>1</value>
</choice>
</attribute>
</optional>
</define>
<define name="attlist.field" combine="interleave">
<optional>
<attribute a:defaultValue="1">
<name ns="">delete</name>
<choice>
<value>0</value>
<value>1</value>
</choice>
</attribute>
</optional>
</define>
<define name="attlist.field" combine="interleave">
<optional>
<attribute a:defaultValue="left_to_right">
<name ns="">orientation</name>
<choice>
<value>left_to_right</value>
<value>right_to_left</value>
<value>bottom_to_top</value>
<value>top_to_bottom</value>
</choice>
</attribute>
</optional>
</define>
<define name="attlist.field" combine="interleave">
<optional>
<attribute a:defaultValue="0">
<name ns="">pre_validate</name>
<choice>
<value>0</value>
<value>1</value>
</choice>
</attribute>
</optional>
</define>
<define name="attlist.field" combine="interleave">
<optional>
<attribute a:defaultValue="1">
<name ns="">completion</name>
<choice>
<value>0</value>
<value>1</value>
</choice>
</attribute>
</optional>
</define>
<define name="attlist.field" combine="interleave">
<optional>
<attribute>
<name ns="">string</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.field" combine="interleave">
<optional>
<attribute a:defaultValue="1">
<name ns="">factor</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.field" combine="interleave">
<optional>
<attribute>
<name ns="">filename</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.field" combine="interleave">
<optional>
<attribute>
<name ns="">help_field</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.field" combine="interleave">
<optional>
<attribute>
<name ns="">view_ids</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.field" combine="interleave">
<optional>
<attribute>
<name ns="">symbol</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.field" combine="interleave">
<optional>
<attribute a:defaultValue="1">
<name ns="">grouping</name>
<choice>
<value>0</value>
<value>1</value>
</choice>
</attribute>
</optional>
</define>
<define name="prefix">
<element>
<name ns="">prefix</name>
<ref name="attlist.affix"/>
<empty/>
</element>
</define>
<define name="suffix">
<element>
<name ns="">suffix</name>
<ref name="attlist.affix"/>
<empty/>
</element>
</define>
<define name="attlist.affix" combine="interleave">
<optional>
<attribute>
<name ns="">string</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.affix" combine="interleave">
<choice>
<attribute>
<name ns="">name</name>
<text/>
</attribute>
<attribute>
<name ns="">id</name>
<text/>
</attribute>
</choice>
</define>
<define name="attlist.affix" combine="interleave">
<optional>
<attribute>
<name ns="">icon</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.affix" combine="interleave">
<optional>
<attribute a:defaultValue="icon">
<name ns="">icon_type</name>
<choice>
<value>icon</value>
<value>url</value>
</choice>
</attribute>
</optional>
</define>
<define name="attlist.affix" combine="interleave">
<optional>
<attribute>
<name ns="">url_size</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.affix" combine="interleave">
<optional>
<attribute a:defaultValue="square">
<name ns="">border</name>
<choice>
<value>square</value>
<value>circle</value>
<value>rounded</value>
</choice>
</attribute>
</optional>
</define>
<define name="button">
<element>
<name ns="">button</name>
<ref name="attlist.button"/>
<empty/>
</element>
</define>
<define name="attlist.button" combine="interleave">
<optional>
<attribute>
<name ns="">help</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.button" combine="interleave">
<optional>
<attribute>
<name ns="">string</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.button" combine="interleave">
<optional>
<attribute>
<name ns="">confirm</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.button" combine="interleave">
<attribute>
<name ns="">name</name>
<text/>
</attribute>
</define>
<define name="attlist.button" combine="interleave">
<optional>
<attribute>
<name ns="">states</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.button" combine="interleave">
<optional>
<attribute a:defaultValue="action">
<name ns="">keyword</name>
<choice>
<value>relate</value>
<value>print</value>
<value>action</value>
</choice>
</attribute>
</optional>
</define>
<define name="attlist.button" combine="interleave">
<optional>
<attribute>
<name ns="">change</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.button" combine="interleave">
<optional>
<attribute>
<name ns="">type</name>
<choice>
<value>class</value>
<value>instance</value>
</choice>
</attribute>
</optional>
</define>
<define name="attlist.button" combine="interleave">
<optional>
<attribute a:defaultValue="0">
<name ns="">tree_invisible</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.button" combine="interleave">
<optional>
<attribute>
<name ns="">width</name>
<text/>
</attribute>
</optional>
</define>
<define name="data">
<element>
<name ns="">data</name>
<ref name="attlist.data"/>
<zeroOrMore>
<ref name="xpath"/>
</zeroOrMore>
</element>
</define>
<define name="attlist.data" combine="interleave">
<empty/>
</define>
<define name="xpath">
<element>
<name ns="">xpath</name>
<ref name="attlist.xpath"/>
<zeroOrMore>
<choice>
<ref name="field"/>
<ref name="prefix"/>
<ref name="suffix"/>
<ref name="button"/>
<ref name="tree"/>
</choice>
</zeroOrMore>
</element>
</define>
<define name="attlist.xpath" combine="interleave">
<attribute>
<name ns="">expr</name>
<text/>
</attribute>
</define>
<define name="attlist.xpath" combine="interleave">
<optional>
<attribute a:defaultValue="inside">
<name ns="">position</name>
<choice>
<value>inside</value>
<value>replace</value>
<value>replace_attributes</value>
<value>after</value>
<value>before</value>
</choice>
</attribute>
</optional>
</define>
<start>
<choice>
<ref name="tree"/>
<ref name="data"/>
</choice>
</start>
</grammar>

50
ir/ui/ui.xml Executable file
View File

@@ -0,0 +1,50 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<tryton>
<data>
<record model="ir.ui.icon" id="board_icon">
<field name="name">tryton-board</field>
<field name="path">ui/icons/tryton-board.svg</field>
</record>
<record model="ir.ui.icon" id="calendar_icon">
<field name="name">tryton-calendar</field>
<field name="path">ui/icons/tryton-calendar.svg</field>
</record>
<record model="ir.ui.icon" id="folder_icon">
<field name="name">tryton-folder</field>
<field name="path">ui/icons/tryton-folder.svg</field>
</record>
<record model="ir.ui.icon" id="form_icon">
<field name="name">tryton-form</field>
<field name="path">ui/icons/tryton-form.svg</field>
</record>
<record model="ir.ui.icon" id="graph_icon">
<field name="name">tryton-graph</field>
<field name="path">ui/icons/tryton-graph.svg</field>
</record>
<record model="ir.ui.icon" id="list_icon">
<field name="name">tryton-list</field>
<field name="path">ui/icons/tryton-list.svg</field>
</record>
<record model="ir.ui.icon" id="settings_icon">
<field name="name">tryton-settings</field>
<field name="path">ui/icons/tryton-settings.svg</field>
</record>
<record model="ir.ui.icon" id="tree_icon">
<field name="name">tryton-tree</field>
<field name="path">ui/icons/tryton-tree.svg</field>
</record>
<menuitem
name="Administration"
sequence="9999"
id="menu_administration"
icon="tryton-settings"/>
<menuitem
name="User Interface"
parent="menu_administration"
sequence="50"
id="menu_ui"/>
</data>
</tryton>

797
ir/ui/view.py Executable file
View File

@@ -0,0 +1,797 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
import json
import os
import logging
from lxml import etree
from sql import Literal, Null
from trytond.cache import Cache, MemoryCache
from trytond.i18n import gettext
from trytond.model import Index, ModelSQL, ModelView, fields, sequence_ordered
from trytond.model.exceptions import ValidationError
from trytond.pool import Pool
from trytond.pyson import PYSON, Bool, Eval, If, PYSONDecoder
from trytond.rpc import RPC
from trytond.tools import file_open
from trytond.transaction import Transaction
from trytond.wizard import Button, StateView, Wizard
from ..action import DomainError, ViewError
logger = logging.getLogger(__name__)
class XMLError(ValidationError):
pass
class View(
fields.fmany2one(
'model_ref', 'model', 'ir.model,model', "Model",
ondelete='CASCADE'),
fields.fmany2one(
'field_children', 'field_childs,model',
'ir.model.field,name,model',
"Children Field",
domain=[
('model', '=', Eval('model')),
],
states={
'invisible': Eval('type') != 'tree',
}),
fields.fmany2one(
'module_ref', 'module', 'ir.module,name', "Module",
readonly=True, ondelete='CASCADE'),
sequence_ordered('priority', "Priority"), ModelSQL, ModelView):
"View"
__name__ = 'ir.ui.view'
model = fields.Char('Model', states={
'required': Eval('type') != 'board',
})
type = fields.Selection([
(None, ''),
('tree', 'Tree'),
('form', 'Form'),
('graph', 'Graph'),
('calendar', 'Calendar'),
('board', 'Board'),
('list-form', "List Form"),
], 'View Type',
domain=[
If(Bool(Eval('inherit')),
('type', '=', None),
('type', '!=', None)),
],
depends=['inherit'])
type_string = type.translated('type')
data = fields.Text('Data')
name = fields.Char('Name', states={
'invisible': ~(Eval('module') & Eval('name')),
}, depends=['module'], readonly=True)
arch = fields.Function(fields.Text('View Architecture', states={
'readonly': Bool(Eval('name')),
}, depends=['name']), 'get_arch', setter='set_arch')
basis = fields.Function(fields.Boolean("Basis"), 'get_basis')
inherit = fields.Many2One('ir.ui.view', 'Inherited View',
ondelete='CASCADE')
extensions = fields.One2Many(
'ir.ui.view', 'inherit', "Extensions",
filter=[
('basis', '=', False),
],
domain=[
('model', '=', Eval('model')),
('type', '=', None),
],
states={
'invisible': ~Eval('type'),
},
order=[('id', None)])
field_childs = fields.Char('Children Field', states={
'invisible': Eval('type') != 'tree',
}, depends=['type'])
module = fields.Char('Module', states={
'invisible': ~Eval('module'),
}, readonly=True)
domain = fields.Char('Domain', states={
'invisible': ~Eval('inherit'),
}, depends=['inherit'])
_get_rng_cache = MemoryCache('ir_ui_view.get_rng', context=False)
_view_get_cache = Cache('ir_ui_view.view_get')
__module_index = None
@classmethod
def __setup__(cls):
super(View, cls).__setup__()
table = cls.__table__()
cls.priority.required = True
cls.__rpc__['view_get'] = RPC(instantiate=0, cache=dict(days=1))
cls._buttons.update({
'show': {
'readonly': Eval('type') != 'form',
'invisible': ~Eval('basis', False),
'depends': ['type', 'basis'],
},
})
cls._sql_indexes.update({
Index(table,
(table.model, Index.Equality()),
(table.inherit, Index.Equality())),
Index(
table,
(table.id, Index.Equality()),
(table.inherit, Index.Equality())),
})
@staticmethod
def default_priority():
return 16
@staticmethod
def default_module():
return Transaction().context.get('module') or ''
def get_basis(self, name):
return not self.inherit or self.model != self.inherit.model
@classmethod
def domain_basis(cls, domain, tables):
table, _ = tables[None]
if 'inherit' not in tables:
inherit = cls.__table__()
tables['inherit'] = {
None: (inherit, table.inherit == inherit.id),
}
else:
inherit, _ = tables['inherit'][None]
expression = (table.inherit == Null) | (table.model != inherit.model)
_, operator, value = domain
if operator in {'=', '!='}:
if (operator == '=') != value:
expression = ~expression
elif operator in {'in', 'not in'}:
if True in value and False not in value:
pass
elif False in value and True not in value:
expression = ~expression
else:
expression = Literal(True)
else:
expression = Literal(True)
return expression
def get_rec_name(self, name):
return '%s (%s)' % (
self.model_ref.rec_name,
self.inherit.rec_name if self.inherit else self.type_string)
@classmethod
def search_rec_name(cls, name, clause):
return [('model_ref.rec_name', *clause[1:])]
@classmethod
@ModelView.button_action('ir.act_view_show')
def show(cls, views):
pass
@classmethod
def get_rng(cls, type_):
key = (cls.__name__, type_)
rng = cls._get_rng_cache.get(key)
if rng is None:
if type_ == 'list-form':
type_ = 'form'
rng_name = os.path.join(os.path.dirname(__file__), type_ + '.rng')
with open(rng_name, 'rb') as fp:
rng = etree.fromstring(fp.read())
cls._get_rng_cache.set(key, rng)
return rng
@property
def rng_type(self):
if self.inherit:
return self.inherit.rng_type
return self.type
@classmethod
def validate(cls, views):
super(View, cls).validate(views)
cls.check_xml(views)
@classmethod
def check_xml(cls, views):
"Check XML"
for view in views:
if not view.arch:
continue
xml = view.arch.strip()
if not xml:
continue
tree = etree.fromstring(xml)
if hasattr(etree, 'RelaxNG'):
validator = etree.RelaxNG(etree=cls.get_rng(view.rng_type))
if not validator.validate(tree):
error_log = '\n'.join(map(str,
validator.error_log.filter_from_errors()))
raise XMLError(
gettext('ir.msg_view_invalid_xml', name=view.rec_name),
error_log)
root_element = tree.getroottree().getroot()
# validate pyson attributes
validates = {
'states': fields.states_validate,
}
def encode(element):
for attr in ('states', 'domain', 'spell'):
if not element.get(attr):
continue
try:
value = PYSONDecoder().decode(element.get(attr))
validates.get(attr, lambda a: True)(value)
except Exception as e:
error_log = '%s: <%s %s="%s"/>' % (
e, element.get('id') or element.get('name'), attr,
element.get(attr))
raise XMLError(
gettext(
'ir.msg_view_invalid_xml', name=view.rec_name),
error_log) from e
for child in element:
encode(child)
encode(root_element)
def get_arch(self, name):
value = None
if self.name and self.module:
path = os.path.join(self.module, 'view', self.name + '.xml')
try:
with file_open(path,
subdir='modules', mode='r', encoding='utf-8') as fp:
value = fp.read()
except IOError:
pass
if not value:
value = self.data
return value
@classmethod
def set_arch(cls, views, name, value):
cls.write(views, {'data': value})
@classmethod
def delete(cls, views):
super(View, cls).delete(views)
# Restart the cache
cls._view_get_cache.clear()
ModelView._fields_view_get_cache.clear()
@classmethod
def create(cls, vlist):
views = super(View, cls).create(vlist)
# Restart the cache
cls._view_get_cache.clear()
ModelView._fields_view_get_cache.clear()
return views
@classmethod
def write(cls, views, values, *args):
super(View, cls).write(views, values, *args)
# Restart the cache
cls._view_get_cache.clear()
ModelView._fields_view_get_cache.clear()
@property
def _module_index(self):
from trytond.modules import create_graph, get_modules
if self.__class__.__module_index is None:
graph = create_graph(get_modules(with_test=Pool.test))
modules = [m.name for m in graph]
self.__class__.__module_index = {
m: i for i, m in enumerate(reversed(modules))}
return self.__class__.__module_index
def view_get(self, model=None):
key = (self.id, model)
result = self._view_get_cache.get(key)
if result:
return result
if self.inherit:
if self.inherit.model == model:
return self.inherit.view_get(model=model)
else:
arch = self.inherit.view_get(self.inherit.model)['arch']
else:
arch = self.arch
views = self.__class__.search(['OR', [
('inherit', '=', self.id),
('model', '=', model),
], [
('id', '=', self.id),
('inherit', '!=', None),
],
])
views.sort(
key=lambda v: self._module_index.get(v.module, -1), reverse=True)
parser = etree.XMLParser(remove_comments=True, resolve_entities=False)
# logger.info("ARCH:%s",arch)
# logger.info("MODEL:%s",model)
# logger.info("PARSER:%s",etree)
tree = etree.fromstring(arch, parser=parser)
decoder = PYSONDecoder({'context': Transaction().context})
for view in views:
if view.domain and not decoder.decode(view.domain):
continue
if not view.arch or not view.arch.strip():
continue
tree_inherit = etree.fromstring(view.arch, parser=parser)
tree = self.inherit_apply(tree, tree_inherit)
if model:
root = tree.getroottree().getroot()
self._translate(root, model, Transaction().language)
arch = etree.tostring(tree, encoding='utf-8').decode('utf-8')
result = {
'type': self.rng_type,
'view_id': self.id,
'arch': arch,
'field_childs': self.field_childs,
}
self._view_get_cache.set(key, result)
return result
@classmethod
def inherit_apply(cls, tree, inherit):
root_inherit = inherit.getroottree().getroot()
for element in root_inherit:
expr = element.get('expr')
targets = tree.xpath(expr)
assert targets, "No elements found for expression %r" % expr
for target in targets:
position = element.get('position', 'inside')
new_tree = getattr(cls, '_inherit_apply_%s' % position)(
tree, element, target)
if new_tree:
tree = new_tree
return tree
@classmethod
def _inherit_apply_replace(cls, tree, element, target):
parent = target.getparent()
if parent is None:
tree, = element
return tree
cls._inherit_apply_after(tree, element, target)
parent.remove(target)
@classmethod
def _inherit_apply_replace_attributes(cls, tree, element, target):
child, = element
for attr in child.attrib:
target.set(attr, child.get(attr))
@classmethod
def _inherit_apply_inside(cls, tree, element, target):
target.extend(list(element))
@classmethod
def _inherit_apply_after(cls, tree, element, target):
parent = target.getparent()
next_ = target.getnext()
if next_ is not None:
for child in element:
index = parent.index(next_)
parent.insert(index, child)
else:
parent.extend(list(element))
@classmethod
def _inherit_apply_before(cls, tree, element, target):
parent = target.getparent()
for child in element:
index = parent.index(target)
parent.insert(index, child)
@classmethod
def _translate(cls, element, model, language):
pool = Pool()
Translation = pool.get('ir.translation')
for attr in ['string', 'sum', 'confirm', 'help']:
if element.get(attr):
translation = Translation.get_source(
model, 'view', language, element.get(attr))
if translation:
element.set(attr, translation)
for child in element:
cls._translate(child, model, language)
class ShowViewStart(ModelView):
'Show view'
__name__ = 'ir.ui.view.show.start'
__no_slots__ = True
class ShowView(Wizard):
'Show view'
__name__ = 'ir.ui.view.show'
class ShowStateView(StateView):
def __init__(self, model_name, buttons):
StateView.__init__(self, model_name, None, buttons)
def get_view(self, wizard, state_name):
pool = Pool()
View = pool.get('ir.ui.view')
view_id = Transaction().context.get('active_id')
if not view_id:
# Set type to please ModuleTestCase.test_wizards
return {'type': 'form'}
view = View(view_id)
Model = pool.get(view.model)
return Model.fields_view_get(view_id=view.id)
def get_defaults(self, wizard, state_name, fields):
return {}
start = ShowStateView('ir.ui.view.show.start', [
Button('Close', 'end', 'tryton-close', default=True),
])
class ViewTreeWidth(
fields.fmany2one(
'model_ref', 'model', 'ir.model,model', "Model",
required=True, ondelete='CASCADE'),
fields.fmany2one(
'field_ref', 'field,model', 'ir.model.field,name,model', "Field",
required=True, ondelete='CASCADE',
domain=[
('model', '=', Eval('model')),
]),
ModelSQL, ModelView):
"View Tree Width"
__name__ = 'ir.ui.view_tree_width'
model = fields.Char('Model', required=True)
field = fields.Char('Field', required=True)
user = fields.Many2One('res.user', 'User', required=True,
ondelete='CASCADE')
width = fields.Integer('Width', required=True)
@classmethod
def __setup__(cls):
super(ViewTreeWidth, cls).__setup__()
table = cls.__table__()
cls.__rpc__.update({
'set_width': RPC(readonly=False),
})
cls._sql_indexes.add(
Index(
table,
(table.user, Index.Equality()),
(table.model, Index.Equality()),
(table.field, Index.Equality())))
def get_rec_name(self, name):
return f'{self.field_ref.rec_name} @ {self.model_ref.rec_name}'
@classmethod
def search_rec_name(cls, name, clause):
operator = clause[1]
if operator.startswith('!') or operator.startswith('not '):
bool_op = 'AND'
else:
bool_op = 'OR'
return [bool_op,
('model_ref.rec_name', *clause[1:]),
('field_ref.rec_name', *clause[1:]),
]
@classmethod
def delete(cls, records):
ModelView._fields_view_get_cache.clear()
super(ViewTreeWidth, cls).delete(records)
@classmethod
def create(cls, vlist):
res = super(ViewTreeWidth, cls).create(vlist)
ModelView._fields_view_get_cache.clear()
return res
@classmethod
def write(cls, records, values, *args):
super(ViewTreeWidth, cls).write(records, values, *args)
ModelView._fields_view_get_cache.clear()
@classmethod
def set_width(cls, model, fields):
'''
Set width for the current user on the model.
fields is a dictionary with key: field name and value: width.
'''
records = cls.search([
('user', '=', Transaction().user),
('model', '=', model),
('field', 'in', list(fields.keys())),
])
cls.delete(records)
to_create = []
for field in list(fields.keys()):
to_create.append({
'model': model,
'field': field,
'user': Transaction().user,
'width': fields[field],
})
if to_create:
cls.create(to_create)
class ViewTreeOptional(ModelSQL, ModelView):
"View Tree Optional"
__name__ = 'ir.ui.view_tree_optional'
view_id = fields.Many2One(
'ir.ui.view', "View ID", required=True, ondelete='CASCADE')
user = fields.Many2One(
'res.user', "User", required=True, ondelete='CASCADE')
field = fields.Char("Field", required=True)
value = fields.Boolean("Value")
@classmethod
def __setup__(cls):
super().__setup__()
cls.__rpc__.update({
'set_optional': RPC(readonly=False),
})
table = cls.__table__()
cls._sql_indexes.add(
Index(
table,
(table.user, Index.Equality()),
(table.view_id, Index.Equality())))
@classmethod
def validate_fields(cls, records, fields_names):
super().validate_fields(records, fields_names)
cls.check_view_id(records, fields_names)
@classmethod
def check_view_id(cls, records, fields_names=None):
if fields_names and 'view_id' not in fields_names:
return
for record in records:
if record.view_id and record.view_id.rng_type != 'tree':
raise ViewError(gettext(
'ir.msg_view_tree_optional_type',
view=record.view_id.rec_name))
@classmethod
def create(cls, vlist):
records = super().create(vlist)
ModelView._fields_view_get_cache.clear()
return records
@classmethod
def write(cls, *args):
super().write(*args)
ModelView._fields_view_get_cache.clear()
@classmethod
def delete(cls, records):
ModelView._fields_view_get_cache.clear()
super().delete(records)
@classmethod
def set_optional(cls, view_id, fields):
"Store optional field that must be displayed"
user = Transaction().user
records = cls.search([
('view_id', '=', view_id),
('user', '=', user),
])
cls.delete(records)
to_create = []
for field, value in fields.items():
to_create.append({
'view_id': view_id,
'user': user,
'field': field,
'value': bool(value),
})
if to_create:
cls.create(to_create)
class ViewTreeState(ModelSQL, ModelView):
'View Tree State'
__name__ = 'ir.ui.view_tree_state'
_rec_name = 'model'
model = fields.Char('Model', required=True)
domain = fields.Char('Domain', required=True)
user = fields.Many2One('res.user', 'User', required=True,
ondelete='CASCADE')
child_name = fields.Char('Child Name')
nodes = fields.Text('Expanded Nodes')
selected_nodes = fields.Text('Selected Nodes')
@classmethod
def __setup__(cls):
super(ViewTreeState, cls).__setup__()
cls.__rpc__.update({
'set': RPC(readonly=False, check_access=False),
'get': RPC(check_access=False, cache=dict(days=1)),
})
table = cls.__table__()
cls._sql_indexes.add(
Index(
table,
(table.user, Index.Equality()),
(table.model, Index.Equality()),
(table.child_name, Index.Equality()),
(table.domain, Index.Equality())))
@staticmethod
def default_nodes():
return '[]'
@staticmethod
def default_selected_nodes():
return '[]'
@classmethod
def set(cls, model, domain, child_name, nodes, selected_nodes):
# Normalize the json domain
logger.info("VIEWTREESTATE:%s",domain)
logger.info("VIEWTREESTATE2:%s",child_name)
logger.info("VIEWTREESTATE3:%s",nodes)
logger.info("VIEWTREESTATE4:%s",cls.__table__())
domain = json.dumps(json.loads(domain), separators=(',', ':'))
current_user = Transaction().user
records = cls.search([
('user', '=', current_user),
('model', '=', model),
('domain', '=', domain),
('child_name', '=', child_name),
])
cls.delete(records)
cls.create([{
'user': current_user,
'model': model,
'domain': domain,
'child_name': child_name,
'nodes': nodes,
'selected_nodes': selected_nodes,
}])
@classmethod
def get(cls, model, domain, child_name):
# Normalize the json domain
domain = json.dumps(json.loads(domain), separators=(',', ':'))
current_user = Transaction().user
try:
expanded_info, = cls.search([
('user', '=', current_user),
('model', '=', model),
('domain', '=', domain),
('child_name', '=', child_name),
],
limit=1)
except ValueError:
return (cls.default_nodes(), cls.default_selected_nodes())
state = cls(expanded_info)
return (state.nodes or cls.default_nodes(),
state.selected_nodes or cls.default_selected_nodes())
class ViewSearch(ModelSQL, ModelView):
"View Search"
__name__ = 'ir.ui.view_search'
name = fields.Char('Name', required=True)
model = fields.Char('Model', required=True)
domain = fields.Char('Domain', help="The PYSON domain.")
user = fields.Many2One('res.user', 'User', ondelete='CASCADE')
@classmethod
def __setup__(cls):
super(ViewSearch, cls).__setup__()
cls.__rpc__.update({
'get': RPC(check_access=False),
'set': RPC(check_access=False, readonly=False),
'unset': RPC(check_access=False, readonly=False),
})
@classmethod
def __register__(cls, module):
super().__register__(module)
table_h = cls.__table_handler__(module)
# Migration from 5.6: remove user required
table_h.not_null_action('user', 'remove')
@staticmethod
def default_user():
return Transaction().user
@classmethod
def validate_fields(cls, searches, field_names):
super().validate_fields(searches, field_names)
cls.check_domain(searches, field_names)
@classmethod
def check_domain(cls, searches, field_names):
decoder = PYSONDecoder()
if field_names and 'domain' not in field_names:
return
for search in searches:
try:
value = decoder.decode(search.domain)
except Exception as exception:
raise DomainError(
gettext('ir.msg_view_search_invalid_domain',
domain=search.domain,
search=search.rec_name)) from exception
if isinstance(value, PYSON):
if not value.types() == set([list]):
raise DomainError(
gettext('ir.msg_view_search_invalid_domain',
domain=search.domain,
search=search.rec_name))
elif not isinstance(value, list):
raise DomainError(
gettext('ir.msg_view_search_invalid_domain',
domain=search.domain,
search=search.rec_name))
else:
try:
fields.domain_validate(value)
except Exception as exception:
raise DomainError(
gettext('ir.msg_view_search_invalid_domain',
domain=search.domain,
search=search.rec_name)) from exception
@classmethod
def get(cls):
decoder = PYSONDecoder()
user = Transaction().user
searches = cls.search_read(['OR',
('user', '=', user),
('user', '=', None),
],
order=[('model', 'ASC'), ('name', 'ASC')],
fields_names=['id', 'name', 'model', 'domain', '_delete'])
result = {}
for search in searches:
result.setdefault(search['model'], []).append((
search['id'],
search['name'],
decoder.decode(search['domain']),
search['_delete']))
return result
@classmethod
def set(cls, name, model, domain):
user = Transaction().user
search, = cls.create([{
'name': name,
'model': model,
'domain': domain,
'user': user,
}])
return search.id
@classmethod
def unset(cls, id):
user = Transaction().user
cls.delete(cls.search([
('id', '=', id),
('user', '=', user),
]))

191
ir/ui/view.xml Executable file
View File

@@ -0,0 +1,191 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<tryton>
<data>
<record model="ir.action.wizard" id="act_view_show">
<field name="name">Show View</field>
<field name="wiz_name">ir.ui.view.show</field>
</record>
<record model="ir.ui.view" id="view_view_form">
<field name="model">ir.ui.view</field>
<field name="type">form</field>
<field name="name">ui_view_form</field>
</record>
<record model="ir.ui.view" id="view_view_tree">
<field name="model">ir.ui.view</field>
<field name="type">tree</field>
<field name="priority" eval="10"/>
<field name="name">ui_view_list</field>
</record>
<record model="ir.ui.view" id="view_view_list_extension">
<field name="model">ir.ui.view</field>
<field name="type">tree</field>
<field name="priority" eval="20"/>
<field name="name">ui_view_list_extension</field>
</record>
<record model="ir.action.act_window" id="act_view_form">
<field name="name">Views</field>
<field name="type">ir.action.act_window</field>
<field name="res_model">ir.ui.view</field>
<field name="domain" eval="[('basis', '=', True)]" pyson="1"/>
<field name="order" eval="[('model', None)]" pyson="1"/>
</record>
<record model="ir.action.act_window.view"
id="act_view_form_view1">
<field name="sequence" eval="1"/>
<field name="view" ref="view_view_tree"/>
<field name="act_window" ref="act_view_form"/>
</record>
<record model="ir.action.act_window.view"
id="act_view_form_view2">
<field name="sequence" eval="2"/>
<field name="view" ref="view_view_form"/>
<field name="act_window" ref="act_view_form"/>
</record>
<menuitem
parent="menu_ui"
action="act_view_form"
sequence="10"
id="menu_view"/>
<record model="ir.model.button" id="view_show_button">
<field name="model">ir.ui.view</field>
<field name="name">show</field>
<field name="string">Show</field>
</record>
<record model="ir.ui.view" id="view_tree_width_view_form">
<field name="model">ir.ui.view_tree_width</field>
<field name="type">form</field>
<field name="name">ui_view_tree_width_form</field>
</record>
<record model="ir.ui.view" id="view_tree_width_view_tree">
<field name="model">ir.ui.view_tree_width</field>
<field name="type">tree</field>
<field name="name">ui_view_tree_width_list</field>
</record>
<record model="ir.action.act_window" id="act_view_tree_width_form">
<field name="name">View Tree Width</field>
<field name="type">ir.action.act_window</field>
<field name="res_model">ir.ui.view_tree_width</field>
</record>
<record model="ir.action.act_window.view"
id="act_view_tree_width_form_view1">
<field name="sequence" eval="1"/>
<field name="view" ref="view_tree_width_view_tree"/>
<field name="act_window" ref="act_view_tree_width_form"/>
</record>
<record model="ir.action.act_window.view"
id="act_view_tree_width_form_view2">
<field name="sequence" eval="2"/>
<field name="view" ref="view_tree_width_view_form"/>
<field name="act_window" ref="act_view_tree_width_form"/>
</record>
<menuitem
parent="menu_ui"
action="act_view_tree_width_form"
sequence="50"
id="menu_view_tree_width"/>
<record model="ir.ui.view" id="view_tree_optional_view_form">
<field name="model">ir.ui.view_tree_optional</field>
<field name="type">form</field>
<field name="name">ui_view_tree_optional_form</field>
</record>
<record model="ir.ui.view" id="view_tree_optional_view_tree">
<field name="model">ir.ui.view_tree_optional</field>
<field name="type">tree</field>
<field name="name">ui_view_tree_optional_list</field>
</record>
<record model="ir.action.act_window" id="act_view_tree_optional_form">
<field name="name">View Tree Optional</field>
<field name="res_model">ir.ui.view_tree_optional</field>
</record>
<record model="ir.action.act_window.view" id="act_view_tree_optional_form_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="view_tree_optional_view_tree"/>
<field name="act_window" ref="act_view_tree_optional_form"/>
</record>
<record model="ir.action.act_window.view" id="act_view_tree_optional_form_view2">
<field name="sequence" eval="20"/>
<field name="view" ref="view_tree_optional_view_form"/>
<field name="act_window" ref="act_view_tree_optional_form"/>
</record>
<menuitem
parent="menu_ui"
action="act_view_tree_optional_form"
sequence="50"
id="menu_view_tree_optional"/>
<record model="ir.ui.view" id="view_tree_state_form">
<field name="model">ir.ui.view_tree_state</field>
<field name="type">form</field>
<field name="name">ui_view_tree_state_form</field>
</record>
<record model="ir.ui.view" id="view_tree_state_tree">
<field name="model">ir.ui.view_tree_state</field>
<field name="type">tree</field>
<field name="name">ui_view_tree_state_list</field>
</record>
<record model="ir.action.act_window" id="act_view_tree_state">
<field name="name">Tree State</field>
<field name="type">ir.action.act_window</field>
<field name="res_model">ir.ui.view_tree_state</field>
</record>
<record model="ir.action.act_window.view"
id="act_view_tree_state_tree">
<field name="sequence" eval="10"/>
<field name="view" ref="view_tree_state_tree"/>
<field name="act_window" ref="act_view_tree_state"/>
</record>
<record model="ir.action.act_window.view"
id="act_view_tree_state_form">
<field name="sequence" eval="20"/>
<field name="view" ref="view_tree_state_form"/>
<field name="act_window" ref="act_view_tree_state"/>
</record>
<menuitem
parent="menu_ui"
action="act_view_tree_state"
sequence="50"
id="menu_view_tree_state"/>
<record model="ir.ui.view" id="view_search_form">
<field name="model">ir.ui.view_search</field>
<field name="type">form</field>
<field name="name">ui_view_search_form</field>
</record>
<record model="ir.ui.view" id="view_search_tree">
<field name="model">ir.ui.view_search</field>
<field name="type">tree</field>
<field name="name">ui_view_search_list</field>
</record>
<record model="ir.action.act_window" id="act_view_search">
<field name="name">View Search</field>
<field name="type">ir.action.act_window</field>
<field name="res_model">ir.ui.view_search</field>
</record>
<record model="ir.action.act_window.view"
id="act_view_search_tree">
<field name="sequence" eval="10"/>
<field name="view" ref="view_search_tree"/>
<field name="act_window" ref="act_view_search"/>
</record>
<record model="ir.action.act_window.view"
id="act_view_search_form">
<field name="sequence" eval="20"/>
<field name="view" ref="view_search_form"/>
<field name="act_window" ref="act_view_search"/>
</record>
<menuitem
parent="menu_ui"
action="act_view_search"
sequence="50"
id="menu_view_search"/>
</data>
</tryton>