diff --git a/addons/event_sale/event_demo.xml b/addons/event_sale/event_demo.xml
index 3dee226119b..7ac1d5f2738 100644
--- a/addons/event_sale/event_demo.xml
+++ b/addons/event_sale/event_demo.xml
@@ -68,8 +68,10 @@
- 5000.0
+ 1500.0
10
+ Cathering for breakfast and lunch
+VIP Parking access
@@ -97,8 +99,10 @@
- 5000.0
+ 1500.0
5
+ Cathering for breakfast and lunch
+VIP Parking access
@@ -115,8 +119,10 @@
- 5000.0
+ 1500.0
5
+ Cathering for breakfast and lunch
+VIP Parking access
diff --git a/addons/website/__init__.py b/addons/website/__init__.py
index d9cff35c01a..185183a2257 100644
--- a/addons/website/__init__.py
+++ b/addons/website/__init__.py
@@ -1,5 +1,3 @@
import controllers
-import res_config
-import website
-import view
+import models
diff --git a/addons/website/controllers/main.py b/addons/website/controllers/main.py
index 7b0ae426553..1bdeddb7107 100644
--- a/addons/website/controllers/main.py
+++ b/addons/website/controllers/main.py
@@ -14,7 +14,7 @@ import werkzeug.wrappers
from PIL import Image
import openerp
-from openerp.addons.website import website
+from openerp.addons.website.models import website
from openerp.addons.web import http
from openerp.addons.web.http import request
diff --git a/addons/website/models/__init__.py b/addons/website/models/__init__.py
new file mode 100644
index 00000000000..94d4412b1cf
--- /dev/null
+++ b/addons/website/models/__init__.py
@@ -0,0 +1,6 @@
+# -*- coding: utf-8 -*-
+
+import ir_fields
+import res_config
+import view
+import website
diff --git a/addons/website/models/ir_fields.py b/addons/website/models/ir_fields.py
new file mode 100644
index 00000000000..fbccf9c296d
--- /dev/null
+++ b/addons/website/models/ir_fields.py
@@ -0,0 +1,87 @@
+# -*- coding: utf-8 -*-
+from lxml import etree, html
+
+from openerp.osv import orm, fields
+from openerp.tools import ustr
+
+class converter(orm.Model):
+ _inherit = 'ir.fields.converter'
+
+ def _html_to_integer(self, cr, uid, model, column, value, context=None):
+ return int(value.text_content().strip()), []
+
+ def _html_to_float(self, cr, uid, model, column, value, context=None):
+ return float(value.text_content().strip()), []
+
+ def _html_to_passthrough(self, cr, uid, model, column, value, context=None):
+ return value.text_content().strip(), []
+
+ _html_to_char = _html_to_date = _html_to_datetime = _html_to_passthrough
+
+ def _html_to_text(self, cr, uid, model, column, value, context=None):
+ return value.text_content(), []
+
+ def _html_to_selection(self, cr, uid, model, column, value, context=None):
+ text = value.text_content().strip()
+
+ selection = column.reify(cr, uid, model, column, context=context)
+ for k, v in selection:
+ if isinstance(v, str):
+ v = ustr(v)
+ if text == v:
+ return k, []
+
+ warning = u"No value found for label %s in selection %s" % (text, selection)
+ # FIXME: ?
+ return False, [Warning(warning.encode('utf-8'))]
+
+ def _html_to_many2one(self, cr, uid, model, column, value, context=None):
+ matches = self.pool[column._obj].name_search(
+ cr, uid, name=value.text_content().strip(), context=context)
+ # FIXME: more than one match, error reporting
+ return matches[0][0], []
+
+ def _html_to_html(self, cr, uid, model, column, value, context=None):
+ content = []
+ if value.text: content.append(value.text)
+ content.extend(html.tostring(child)
+ for child in value.iterchildren(tag=etree.Element))
+ return '\n'.join(content), []
+
+
+class test_converter(orm.Model):
+ _name = 'website.converter.test'
+
+ _columns = {
+ 'char': fields.char(),
+ 'integer': fields.integer(),
+ 'float': fields.float(),
+ 'numeric': fields.float(digits=(16, 2)),
+ 'many2one': fields.many2one('website.converter.test.sub'),
+ 'binary': fields.binary(),
+ 'date': fields.date(),
+ 'datetime': fields.datetime(),
+ 'selection': fields.selection([
+ (1, "réponse A"),
+ (2, "réponse B"),
+ (3, "réponse C"),
+ (4, "réponse D"),
+ ]),
+ 'selection_str': fields.selection([
+ ('A', "Qu'il n'est pas arrivé à Toronto"),
+ ('B', "Qu'il était supposé arriver à Toronto"),
+ ('C', "Qu'est-ce qu'il fout ce maudit pancake, tabernacle ?"),
+ ('D', "La réponse D"),
+ ], string="Lorsqu'un pancake prend l'avion à destination de Toronto et "
+ "qu'il fait une escale technique à St Claude, on dit:"),
+ 'html': fields.html(),
+ 'text': fields.text(),
+ }
+
+
+class test_converter_sub(orm.Model):
+ _name = 'website.converter.test.sub'
+
+ _columns = {
+ 'name': fields.char(),
+ }
diff --git a/addons/website/res_config.py b/addons/website/models/res_config.py
similarity index 100%
rename from addons/website/res_config.py
rename to addons/website/models/res_config.py
diff --git a/addons/website/view.py b/addons/website/models/view.py
similarity index 66%
rename from addons/website/view.py
rename to addons/website/models/view.py
index 6290a326fcf..6ddae129185 100644
--- a/addons/website/view.py
+++ b/addons/website/models/view.py
@@ -55,57 +55,19 @@ class view(osv.osv):
def extract_embedded_fields(self, cr, uid, arch, context=None):
return arch.xpath('//*[@data-oe-model != "ir.ui.view"]')
- def convert_embedded_field(self, cr, uid, el, column,
- type_override=None, context=None):
- """ Converts the content of an embedded field to a value acceptable
- for writing in the column
-
- :param etree._Element el: embedded field being saved
- :param fields._column column: column object corresponding to the field
- :param type type_override: column type to dispatch on instead of the
- column's actual type (for proxy column types
- e.g. relateds)
- :return: converted value
- """
- column_type = type_override or type(column)
-
- if issubclass(column_type, fields.html):
- content = []
- if el.text: content.append(el.text)
- content.extend(html.tostring(child)
- for child in el.iterchildren(tag=etree.Element))
- return '\n'.join(content)
- elif issubclass(column_type, fields.integer):
- return int(el.text_content())
- elif issubclass(column_type, fields.float):
- return float(el.text_content())
- elif issubclass(column_type, (fields.char, fields.text,
- fields.date, fields.datetime)):
- return el.text_content()
- # TODO: fields.selection
- elif issubclass(column_type, fields.many2one):
- matches = self.pool[column._obj].name_search(
- cr, uid, name=el.text_content().strip(), context=context)
- # FIXME: more than one match, error reporting
- return matches[0][0]
- elif issubclass(column_type, fields.function):
- # FIXME: special-case selection as in get_pg_type?
- return self.convert_embedded_field(
- cr, uid, el, column,
- type_override=getattr(fields, column._type),
- context=context)
- # TODO?: fields.many2many, fields.one2many
- # TODO?: fields.reference
- else:
- raise TypeError("Un-convertable column type %s" % column_type)
-
def save_embedded_field(self, cr, uid, el, context=None):
Model = self.pool[el.get('data-oe-model')]
field = el.get('data-oe-field')
column = Model._all_columns[field].column
+ convert = self.pool['ir.fields.converter'].to_field(
+ cr, uid, Model, column, fromtype="html", context=context)
+ value, warnings = convert(el)
+ # FIXME: report error
+ if warnings: return
+
Model.write(cr, uid, [int(el.get('data-oe-id'))], {
- field: self.convert_embedded_field(cr, uid, el, column, context=context)
+ field: value
}, context=context)
def to_field_ref(self, cr, uid, el, context=None):
@@ -137,7 +99,8 @@ class view(osv.osv):
"""
res_id = int(res_id)
- arch_section = html.fromstring(value)
+ arch_section = html.fromstring(
+ value, parser=html.HTMLParser(encoding='utf-8'))
if xpath is None:
# value is an embedded field on its own, not a view section
diff --git a/addons/website/website.py b/addons/website/models/website.py
similarity index 100%
rename from addons/website/website.py
rename to addons/website/models/website.py
diff --git a/addons/website/static/src/css/editor.css b/addons/website/static/src/css/editor.css
index 8a52aec47aa..2d1439f4f36 100644
--- a/addons/website/static/src/css/editor.css
+++ b/addons/website/static/src/css/editor.css
@@ -1,4 +1,4 @@
-@charset "utf-8";
+@charset "UTF-8";
/* ---- CKEditor Minimal Reset ---- */
.navbar.navbar-inverse .cke_chrome {
border: none;
@@ -167,7 +167,6 @@ table.editorbar-panel td.selected {
box-shadow: 0px 10px 10px -10px black inset;
z-index: 1010;
overflow: hidden;
- height: 110px;
}
#oe_snippets:hover {
height: auto;
@@ -177,16 +176,13 @@ table.editorbar-panel td.selected {
overflow-y: auto;
}
#oe_snippets .nav {
- padding: 3px 15px;
- width: 162px;
- background: #444444;
position: fixed;
z-index: 1;
- height: 108px;
}
#oe_snippets .nav > li {
- display: block;
+ display: inline-block;
float: none;
+ font-size: 13px;
}
#oe_snippets .nav > li > a {
padding: 2px 10px !important;
@@ -194,13 +190,14 @@ table.editorbar-panel td.selected {
display: block;
}
#oe_snippets .pill-content {
+ margin-top: 24px;
display: inline-block;
- height: 110px;
white-space: nowrap;
- padding-left: 166px;
}
#oe_snippets .pill-content > div {
display: inline-block;
+ background: black;
+ padding: 5px;
}
#oe_snippets .pill-content > div label {
width: 44px;
@@ -211,23 +208,23 @@ table.editorbar-panel td.selected {
#oe_snippets .pill-content > div label div {
width: 100px;
text-align: center;
- -webkit-transform: translate(-39px, 44px) rotate(-90deg);
- -moz-transform: translate(-39px, 44px) rotate(-90deg);
- -o-transform: translate(-39px, 44px) rotate(-90deg);
- transform: translate(-39px, 44px) rotate(-90deg);
- -webkit-transform-origin: 50% 50%;
- -moz-transform-origin: 50% 50%;
- -ms-transform-origin: 50% 50%;
- -o-transform-origin: 50% 50%;
- transform-origin: 50% 50%;
+ -webkit-transform: translate(-39px, 44px);
+ -moz-transform: translate(-39px, 44px);
+ -ms-transform: translate(-39px, 44px);
+ -o-transform: translate(-39px, 44px);
+ transform: translate(-39px, 44px);
+ -webkit-transform-origin: 50% 50% 50%;
+ -moz-transform-origin: 50% 50% 50%;
+ -ms-transform-origin: 50% 50% 50%;
+ -o-transform-origin: 50% 50% 50%;
+ transform-origin: 50% 50% 50%;
}
.oe_snippet {
display: inline-block;
vertical-align: top;
- width: 108px;
- height: 108px;
- border-radius: 3px;
+ width: 100px;
+ height: 100px;
margin: 1px;
margin-top: 0px;
position: relative;
@@ -243,9 +240,15 @@ table.editorbar-panel td.selected {
height: 100%;
background: transparent;
color: white;
- text-shadow: 0px 1px 2px rgba(0, 0, 0, 0.95);
position: relative;
}
+.oe_snippet .oe_snippet_thumbnail .oe_snippet_thumbnail_title {
+ font-size: 12px;
+ display: block;
+}
+.oe_snippet .oe_snippet_thumbnail .oe_snippet_thumbnail_img {
+ box-shadow: inset 0px 0px 0px 3px #333333;
+}
.oe_snippet .oe_snippet_thumbnail span, .oe_snippet .oe_snippet_thumbnail div {
line-height: 18px;
}
@@ -256,7 +259,6 @@ table.editorbar-panel td.selected {
#oe_snippets .oe_snippet_thumbnail {
background: #747474;
background-image: radial-gradient(rgba(0, 0, 0, 0.25), rgba(0, 0, 0, 0.4));
- box-shadow: 0px 0px 0px 1px rgba(255, 255, 255, 0.07) inset;
}
/* ---- SNIPPETS DROP ZONES ---- */
diff --git a/addons/website/static/src/css/editor.sass b/addons/website/static/src/css/editor.sass
index 54c6b6576de..0a126bb3197 100644
--- a/addons/website/static/src/css/editor.sass
+++ b/addons/website/static/src/css/editor.sass
@@ -1,5 +1,7 @@
@charset "utf-8"
+@import "compass/css3"
+
/* ---- CKEditor Minimal Reset ---- */
.navbar.navbar-inverse .cke_chrome
@@ -145,33 +147,30 @@ table.editorbar-panel
box-shadow: 0px 10px 10px -10px black inset
z-index: 1010
overflow: hidden
- height: 110px
&:hover
height: auto
.scroll
white-space: nowrap
overflow-y: auto
.nav
- padding: 3px 15px
- width: 162px
- background: #444444
> li
- display: block
+ display: inline-block
float: none
+ font-size: 13px
> a
padding: 2px 10px !important
width: 100%
display: block
position: fixed
z-index: 1
- height: 108px
.pill-content
+ margin-top: 24px
display: inline-block
- height: 110px
white-space: nowrap
- padding-left: 166px
> div
display: inline-block
+ background: rgb(0,0,0)
+ padding: 5px
label
width: 44px
height: 100%
@@ -180,22 +179,14 @@ table.editorbar-panel
div
width: 100px
text-align: center
- -webkit-transform: translate(-39px, 44px) rotate(-90deg)
- -moz-transform: translate(-39px, 44px) rotate(-90deg)
- -o-transform: translate(-39px, 44px) rotate(-90deg)
- transform: translate(-39px, 44px) rotate(-90deg)
- -webkit-transform-origin: 50% 50%
- -moz-transform-origin: 50% 50%
- -ms-transform-origin: 50% 50%
- -o-transform-origin: 50% 50%
- transform-origin: 50% 50%
+ @include transform( translate(-39px, 44px) , rotate(-90deg) )
+ @include transform-origin(50% 50%)
.oe_snippet
display: inline-block
vertical-align: top
- width: 108px
- height: 108px
- border-radius: 3px
+ width: 100px
+ height: 100px
margin: 1px
margin-top: 0px
position: relative
@@ -204,15 +195,18 @@ table.editorbar-panel
user-select: none
cursor: move
pointer-events: none
-
.oe_snippet_thumbnail
pointer-events: auto
text-align: center
height: 100%
background: transparent
color: white
- text-shadow: 0px 1px 2px rgba(0, 0, 0, 0.95)
position: relative
+ .oe_snippet_thumbnail_title
+ font-size: 12px
+ display: block
+ .oe_snippet_thumbnail_img
+ box-shadow: inset 0px 0px 0px 3px #333333
span, div
line-height: 18px
& > :not(.oe_snippet_thumbnail)
@@ -221,7 +215,6 @@ table.editorbar-panel
#oe_snippets .oe_snippet_thumbnail
background: rgb(116, 116, 116)
background-image: radial-gradient(rgba(0,0,0,0.25),rgba(0,0,0,0.4))
- box-shadow: 0px 0px 0px 1px rgba(255, 255, 255, 0.07) inset
/* ---- SNIPPETS DROP ZONES ---- */
diff --git a/addons/website/static/src/css/website.css b/addons/website/static/src/css/website.css
index 80057dbf21d..1796729f18c 100644
--- a/addons/website/static/src/css/website.css
+++ b/addons/website/static/src/css/website.css
@@ -367,6 +367,25 @@ ul.nav-stacked > li > a {
}
/* ---- PUBLISH ---- */
+a[data-publish] {
+ text-decoration: none !important;
+}
+a[data-publish] .css_unpublish, a[data-publish] .css_publish, a[data-publish] .css_unpublished, a[data-publish] .css_published {
+ display: none;
+}
+a[data-publish][data-publish='off'] .css_unpublished, a[data-publish][data-publish='off']:hover .css_publish {
+ display: inline;
+}
+a[data-publish][data-publish='off']:hover .css_unpublished {
+ display: none;
+}
+a[data-publish][data-publish='on'] .css_published, a[data-publish][data-publish='on']:hover .css_unpublish {
+ display: inline;
+}
+a[data-publish][data-publish='on']:hover .css_published {
+ display: none;
+}
+
.dropdown .css_unpublish, .dropdown .css_publish {
display: none;
}
diff --git a/addons/website/static/src/css/website.sass b/addons/website/static/src/css/website.sass
index b1f43b543a3..9834fb66aed 100644
--- a/addons/website/static/src/css/website.sass
+++ b/addons/website/static/src/css/website.sass
@@ -283,6 +283,20 @@ ul.nav-stacked > li > a
text-transform: uppercase
/* ---- PUBLISH ---- */
+a[data-publish]
+ text-decoration: none !important
+ .css_unpublish, .css_publish, .css_unpublished, .css_published
+ display: none
+ &[data-publish='off']
+ .css_unpublished, &:hover .css_publish
+ display: inline
+ &:hover .css_unpublished
+ display: none
+ &[data-publish='on']
+ .css_published, &:hover .css_unpublish
+ display: inline
+ &:hover .css_published
+ display: none
.dropdown .css_unpublish, .dropdown .css_publish
display: none
diff --git a/addons/website/static/src/img/blocks/block_banner.png b/addons/website/static/src/img/blocks/block_banner.png
new file mode 100644
index 00000000000..305ffb8855b
Binary files /dev/null and b/addons/website/static/src/img/blocks/block_banner.png differ
diff --git a/addons/website/static/src/img/blocks/block_big_picture.png b/addons/website/static/src/img/blocks/block_big_picture.png
new file mode 100644
index 00000000000..8fd913efcdc
Binary files /dev/null and b/addons/website/static/src/img/blocks/block_big_picture.png differ
diff --git a/addons/website/static/src/img/blocks/block_button.png b/addons/website/static/src/img/blocks/block_button.png
new file mode 100644
index 00000000000..8daf48ec3bc
Binary files /dev/null and b/addons/website/static/src/img/blocks/block_button.png differ
diff --git a/addons/website/static/src/img/blocks/block_comparison.png b/addons/website/static/src/img/blocks/block_comparison.png
new file mode 100644
index 00000000000..610f7658e96
Binary files /dev/null and b/addons/website/static/src/img/blocks/block_comparison.png differ
diff --git a/addons/website/static/src/img/blocks/block_faq.png b/addons/website/static/src/img/blocks/block_faq.png
new file mode 100644
index 00000000000..fe0f819143b
Binary files /dev/null and b/addons/website/static/src/img/blocks/block_faq.png differ
diff --git a/addons/website/static/src/img/blocks/block_image_gallery.png b/addons/website/static/src/img/blocks/block_image_gallery.png
new file mode 100644
index 00000000000..2b70ce2557a
Binary files /dev/null and b/addons/website/static/src/img/blocks/block_image_gallery.png differ
diff --git a/addons/website/static/src/img/blocks/block_image_text.png b/addons/website/static/src/img/blocks/block_image_text.png
new file mode 100644
index 00000000000..01bf9861066
Binary files /dev/null and b/addons/website/static/src/img/blocks/block_image_text.png differ
diff --git a/addons/website/static/src/img/blocks/block_jumbotron.png b/addons/website/static/src/img/blocks/block_jumbotron.png
new file mode 100644
index 00000000000..656f9591998
Binary files /dev/null and b/addons/website/static/src/img/blocks/block_jumbotron.png differ
diff --git a/addons/website/static/src/img/blocks/block_panel.png b/addons/website/static/src/img/blocks/block_panel.png
new file mode 100644
index 00000000000..c4d66975905
Binary files /dev/null and b/addons/website/static/src/img/blocks/block_panel.png differ
diff --git a/addons/website/static/src/img/blocks/block_parallax.png b/addons/website/static/src/img/blocks/block_parallax.png
new file mode 100644
index 00000000000..693da297cef
Binary files /dev/null and b/addons/website/static/src/img/blocks/block_parallax.png differ
diff --git a/addons/website/static/src/img/blocks/block_portfolio.png b/addons/website/static/src/img/blocks/block_portfolio.png
new file mode 100644
index 00000000000..40102160f96
Binary files /dev/null and b/addons/website/static/src/img/blocks/block_portfolio.png differ
diff --git a/addons/website/static/src/img/blocks/block_quote.png b/addons/website/static/src/img/blocks/block_quote.png
new file mode 100644
index 00000000000..8c3d082219b
Binary files /dev/null and b/addons/website/static/src/img/blocks/block_quote.png differ
diff --git a/addons/website/static/src/img/blocks/block_references.png b/addons/website/static/src/img/blocks/block_references.png
new file mode 100644
index 00000000000..c7164d36b7a
Binary files /dev/null and b/addons/website/static/src/img/blocks/block_references.png differ
diff --git a/addons/website/static/src/img/blocks/block_separator.png b/addons/website/static/src/img/blocks/block_separator.png
new file mode 100644
index 00000000000..293d83ee398
Binary files /dev/null and b/addons/website/static/src/img/blocks/block_separator.png differ
diff --git a/addons/website/static/src/img/blocks/block_surprise.png b/addons/website/static/src/img/blocks/block_surprise.png
new file mode 100644
index 00000000000..e01382a5ab1
Binary files /dev/null and b/addons/website/static/src/img/blocks/block_surprise.png differ
diff --git a/addons/website/static/src/img/blocks/block_text_block.png b/addons/website/static/src/img/blocks/block_text_block.png
new file mode 100644
index 00000000000..df2c96de997
Binary files /dev/null and b/addons/website/static/src/img/blocks/block_text_block.png differ
diff --git a/addons/website/static/src/img/blocks/block_text_image.png b/addons/website/static/src/img/blocks/block_text_image.png
new file mode 100644
index 00000000000..4e275fcc22a
Binary files /dev/null and b/addons/website/static/src/img/blocks/block_text_image.png differ
diff --git a/addons/website/static/src/img/blocks/block_three_columns.png b/addons/website/static/src/img/blocks/block_three_columns.png
new file mode 100644
index 00000000000..5cc49b3319e
Binary files /dev/null and b/addons/website/static/src/img/blocks/block_three_columns.png differ
diff --git a/addons/website/static/src/img/blocks/block_title.png b/addons/website/static/src/img/blocks/block_title.png
new file mode 100644
index 00000000000..2e28ea0a068
Binary files /dev/null and b/addons/website/static/src/img/blocks/block_title.png differ
diff --git a/addons/website/static/src/img/blocks/blocks-icons.png b/addons/website/static/src/img/blocks/blocks-icons.png
new file mode 100644
index 00000000000..94c82f3f309
Binary files /dev/null and b/addons/website/static/src/img/blocks/blocks-icons.png differ
diff --git a/addons/website/static/src/js/website.editor.js b/addons/website/static/src/js/website.editor.js
index 8b15422b90d..f40ccea715b 100644
--- a/addons/website/static/src/js/website.editor.js
+++ b/addons/website/static/src/js/website.editor.js
@@ -18,7 +18,7 @@
return new website.editor.LinkDialog(editor).appendTo(document.body);
}
function image_dialog(editor) {
- return new website.editor.ImageDialog(editor).appendTo(document.body);
+ return new website.editor.RTEImageDialog(editor).appendTo(document.body);
}
if (website.is_editable) {
@@ -673,6 +673,23 @@
});
},
});
+ /**
+ * ImageDialog widget. Lets users change an image, including uploading a
+ * new image in OpenERP or selecting the image style (if supported by
+ * the caller).
+ *
+ * Initialized as usual, but the caller can hook into two events:
+ *
+ * @event start({url, style}) called during dialog initialization and
+ * opening, the handler can *set* the ``url``
+ * and ``style`` properties on its parameter
+ * to provide these as default values to the
+ * dialog
+ * @event save({url, style}) called during dialog finalization, the handler
+ * is provided with the image url and style
+ * selected by the users (or possibly the ones
+ * originally passed in)
+ */
website.editor.ImageDialog = website.editor.Dialog.extend({
template: 'website.editor.dialog.image',
events: _.extend({}, website.editor.Dialog.prototype.events, {
@@ -685,47 +702,30 @@
'click a[href=#existing]': 'browse_existing',
'change select.image-style': 'preview_image',
}),
- start: function () {
- var selection = this.editor.getSelection();
- var el = selection && selection.getSelectedElement();
- this.element = null;
- var $select = this.$('.image-style');
- var $options = $select.children();
+ start: function () {
+ var $options = this.$('.image-style').children();
this.image_styles = $options.map(function () { return this.value; }).get();
- if (el && el.is('img')) {
- this.element = el;
- _(this.image_styles).each(function (style) {
- if (el.hasClass(style)) {
- $select.val(style);
- }
- });
- // set_image must follow setup of image style
- this.set_image(el.getAttribute('src'));
+ var o = { url: null, style: null, };
+ // avoid typos, prevent addition of new properties to the object
+ Object.preventExtensions(o);
+ this.trigger('start', o);
+
+ if (o.url) {
+ if (o.style) {
+ this.$('.image-style').val(o.style);
+ }
+ this.set_image(o.url);
}
return this._super();
},
save: function () {
- var url = this.$('input.url').val();
- var style = this.$('.image-style').val();
- var element, editor = this.editor;
- if (!(element = this.element)) {
- element = editor.document.createElement('img');
- // focus event handler interactions between bootstrap (modal)
- // and ckeditor (RTE) lead to blowing the stack in Safari and
- // Chrome (but not FF) when this is done synchronously =>
- // defer insertion so modal has been hidden & destroyed before
- // it happens
- setTimeout(function () {
- editor.insertElement(element);
- }, 0);
- }
- element.setAttribute('src', url);
- $(element.$).removeClass(this.image_styles.join(' '));
- if (style) { element.addClass(style); }
-
+ this.trigger('save', {
+ url: this.$('input.url').val(),
+ style: this.$('.image-style').val(),
+ });
return this._super();
},
@@ -769,12 +769,53 @@
.removeClass(this.image_styles.join(' '))
.addClass(this.$('select.image-style').val());
},
-
browse_existing: function (e) {
e.preventDefault();
new website.editor.ExistingImageDialog(this).appendTo(document.body);
},
});
+ website.editor.RTEImageDialog = website.editor.ImageDialog.extend({
+ init: function () {
+ this._super.apply(this, arguments);
+
+ this.on('start', this, this.proxy('started'));
+ this.on('save', this, this.proxy('saved'));
+ },
+ started: function (holder) {
+ var selection = this.editor.getSelection();
+ var el = selection && selection.getSelectedElement();
+ this.element = null;
+
+ if (el && el.is('img')) {
+ this.element = el;
+ _(this.image_styles).each(function (style) {
+ if (el.hasClass(style)) {
+ holder.style = style;
+ }
+ });
+ holder.url = el.getAttribute('src');
+ }
+ },
+ saved: function (data) {
+ var element, editor = this.editor;
+ if (!(element = this.element)) {
+ element = editor.document.createElement('img');
+ // focus event handler interactions between bootstrap (modal)
+ // and ckeditor (RTE) lead to blowing the stack in Safari and
+ // Chrome (but not FF) when this is done synchronously =>
+ // defer insertion so modal has been hidden & destroyed before
+ // it happens
+ setTimeout(function () {
+ editor.insertElement(element);
+ }, 0);
+ }
+
+ var style = data.style;
+ element.setAttribute('src', data.url);
+ $(element.$).removeClass(this.image_styles.join(' '));
+ if (style) { element.addClass(style); }
+ },
+ });
var IMAGES_PER_ROW = 6;
var IMAGES_ROWS = 4;
@@ -893,7 +934,7 @@
if (m.attributeName !== 'class') { return true; }
// find out what classes were added or removed
- var oldClasses = m.oldValue.split(/\s+/);
+ var oldClasses = (m.oldValue || '').split(/\s+/);
var newClasses = m.target.className.split(/\s+/);
var change = _.union(_.difference(oldClasses, newClasses),
_.difference(newClasses, oldClasses));
@@ -911,6 +952,7 @@
while (node && !$(node).hasClass('oe_editable')) {
node = node.parentNode;
}
+ $(m.target).trigger('node_changed');
return node;
})
.compact()
diff --git a/addons/website/static/src/js/website.js b/addons/website/static/src/js/website.js
index bc0db10ce88..e524edfb389 100644
--- a/addons/website/static/src/js/website.js
+++ b/addons/website/static/src/js/website.js
@@ -117,9 +117,29 @@
};
dom_ready.then(function () {
+
/* ----- PUBLISHING STUFF ---- */
+ $('[data-publish]:has(.js_publish)').each(function () {
+ var $pub = $("[data-publish]", this);
+ if($pub.size())
+ $(this).attr("data-publish", $pub.attr("data-publish"));
+ else
+ $(this).removeAttr("data-publish");
+ });
+
$('[data-publish]:has(.js_publish_management)').each(function () {
$(this).attr("data-publish", $(".js_publish_management .btn-success", this).size() ? "on" : 'off');
+ $(this).attr("data-publish", $(".js_publish_management .btn-success", this).size() ? "on" : 'off');
+ });
+
+ $(document).on('click', '.js_publish', function (e) {
+ e.preventDefault();
+ var $data = $(":first", this).parents("[data-publish]");
+ $data.attr("data-publish", $data.first().attr("data-publish") == 'off' ? 'on' : 'off');
+ openerp.jsonRpc('/website/publish', 'call', {'id': $(this).data('id'), 'object': $(this).data('object')})
+ .then(function (result) {
+ $data.attr("data-publish", +result ? 'on' : 'off');
+ });
});
$(document).on('click', '.js_publish_management .js_publish_btn', function (e) {
diff --git a/addons/website/static/src/js/website.snippets.js b/addons/website/static/src/js/website.snippets.js
index 9a6ade27992..3de2ddaeabb 100644
--- a/addons/website/static/src/js/website.snippets.js
+++ b/addons/website/static/src/js/website.snippets.js
@@ -8,8 +8,8 @@
start: function () {
var self = this;
$("[data-oe-model]").on('click', function (event) {
- var tag = event.srcElement.tagName.toLowerCase();
var $this = $(event.srcElement);
+ var tag = $this[0].tagName.toLowerCase();
if (!(tag === 'a' || tag === "button") && !$this.parents("a, button").length) {
self.$('[data-action="edit"]').parent().effect('bounce', {distance: 18, times: 5}, 250);
}
@@ -158,23 +158,29 @@
var $pill = self.$(".pill-content");
var padding = parseInt($pill.css("padding-left"));
var $scroll = this.$(".scroll");
-
- $scroll.scroll(function () {
+
+ $scroll.scroll(function () {
$pill.find("> div").each(function () {
+ /*
if ($(this).position().left <= padding) {
$ul.find("> li").removeClass('active');
$ul.find("a[href='#" + $(this).attr("id") + "']").parent("li").addClass('active');
- }
+ }*/
});
});
$ul.find("a").click(function (event) {
event.preventDefault();
- $scroll.scrollLeft( $scroll.scrollLeft() + $($(event.currentTarget).attr("href")).position().left - padding );
+ $pill.find("> div").hide();
+ $pill.find( $(event.currentTarget).attr('href')).show();
+ $ul.find('> li').removeClass('active');
+ $(event.currentTarget).parent().addClass('active');
return false;
});
- $pill.css("padding-right", self.$el.outerWidth() - padding + 10 - $pill.find("> div:last").outerWidth());
+ // $pill.css("padding-right", self.$el.outerWidth() - padding + 10 - $pill.find("> div:last").outerWidth());
+ $pill.find("> div").hide();
+ $pill.find("div:first").show();
$ul.find("> li:first").addClass('active');
},
cover_target: function ($el, $target){
@@ -389,7 +395,7 @@
},
stop: function(){
$('.oe_drop_zone').droppable('destroy').remove();
- if (!dropped) {
+ if (!dropped && self.$modal.find('input:not(:checked)').length) {
self.$modal.modal('toggle');
}
},
@@ -573,7 +579,6 @@
onBlurEdit : function () {},
});
-
website.snippet.editorRegistry = {};
website.snippet.Editor = openerp.Class.extend({
init: function (parent, dom) {
@@ -700,6 +705,7 @@
this.$overlay.on('click', '.oe_snippet_clone', function () {
var $clone = self.$target.clone(false);
self.$target.after($clone);
+ return false;
});
},
@@ -793,6 +799,50 @@
onBlur : function () {
this.$overlay.removeClass('oe_active');
},
+
+ change_background: function (bg, ul_options) {
+ var self = this;
+ var $ul = this.$editor.find(ul_options);
+ var bg_value = (typeof bg == 'string' ? self.$target.find(bg) : $(bg)).css("background-image").replace(/url\(['"]*|['"]*\)/g, "");
+
+ // select in ul options
+ $ul.find("li").removeClass("active");
+ var selected = $ul.find('[data-value="' + bg_value + '"], [data-value="' + bg_value.replace(/.*:\/\/[^\/]+/, '') + '"]');
+ selected.addClass('active');
+ if (!selected.length) {
+ $ul.find('.oe_custom_bg b').html(bg_value);
+ }
+
+ // bind envent on options
+ var $li = $ul.find("li");
+ $li.on('click', function (event) {
+ if ($(this).data("value")) {
+ $li.removeClass("active");
+ $(this).addClass("active");
+ self.$editor.find('input').val("");
+ } else {
+ var editor = new website.editor.ImageDialog();
+ editor.on('start', self, function (o) {o.url = bg_value;});
+ editor.on('save', self, function (o) {
+ var $bg = typeof bg == 'string' ? self.$target.find(bg) : $(bg);
+ $bg.css("background-image", "url(" + o.url + ")");
+ });
+ editor.appendTo($('body'));
+ }
+ })
+ .on('mouseover', function (event) {
+ if ($(this).data("value")) {
+ var src = $(this).data("value");
+ var $bg = typeof bg == 'string' ? self.$target.find(bg) : $(bg);
+ $bg.css("background-image", "url(" + src + ")");
+ }
+ })
+ .on('mouseout', function (event) {
+ var src = $ul.find('li.active').data("value");
+ var $bg = typeof bg == 'string' ? self.$target.find(bg) : $(bg);
+ $bg.css("background-image", "url(" + src + ")");
+ });
+ },
});
@@ -863,13 +913,13 @@
var change = false;
if (dd > (2*resize[1][next] + resize[1][current])/3) {
- self.$target.attr("class",self.$target.attr("class").replace(regClass, ''));
+ self.$target.attr("class", (self.$target.attr("class")||'').replace(regClass, ''));
self.$target.addClass(resize[0][next]);
current = next;
change = true;
}
if (prev != current && dd < (2*resize[1][prev] + resize[1][current])/3) {
- self.$target.attr("class",self.$target.attr("class").replace(regClass, ''));
+ self.$target.attr("class", (self.$target.attr("class")||'').replace(regClass, ''));
self.$target.addClass(resize[0][prev]);
current = prev;
change = true;
@@ -991,8 +1041,9 @@
this.$editor.find(".js_add").on('click', _.bind(this.on_add, this));
this.$editor.find(".js_remove").on('click', _.bind(this.on_remove, this));
- this.change_background();
+ this.change_background(".item.active", 'ul[name="carousel-background"]');
this.change_style();
+ this.set_options_style();
this.change_size();
},
on_add: function (e) {
@@ -1014,48 +1065,12 @@
.find('.item.active').remove().end()
.find('.item:first').addClass('active');
this.$target.carousel(0);
- this.set_options_background();
this.set_options_style();
}
if (nb <= 1) {
this.$target.find('.carousel-control').addClass("hidden");
}
},
- onFocus: function () {
- this.set_options_background();
- this.set_options_style();
- this._super();
- },
- set_options_background: function () {
- var $image = this.$target.find('.carousel-inner .item.active > img');
- var bg = $image.attr('src');
- var $ul = this.$editor.find('ul[name="carousel-background"]');
- $ul.find("li").removeClass("active");
- var selected = $ul.find('[data-value="' + bg + '"], [data-value="' + bg.replace(/.*:\/\/[^\/]+/, '') + '"]')
- .addClass('active').length;
- this.$editor.find('.oe_custom_bg b').html(selected ? "" : bg);
- },
- change_background: function () {
- var self = this;
- var $ul = this.$editor.find('ul[name="carousel-background"]');
- var $li = $ul.find("li");
-
- $li.on('click', function (event) {
- $li.removeClass("active");
- $(this).addClass("active");
- self.$editor.find('input').val("");
- })
- .on('mouseover', function (event) {
- if ($(this).data("value")) {
- self.$target.find('.carousel-inner .item.active > img')
- .attr('src', $(this).data("value"));
- }
- })
- .on('mouseout', function (event) {
- self.$target.find('.carousel-inner .item.active > img')
- .attr('src', $ul.find('li.active').data("value"));
- });
- },
set_options_style: function () {
var style = false;
var $el = this.$target.find('.carousel-inner .item.active');
@@ -1124,50 +1139,7 @@
website.snippet.editorRegistry.parallax = website.snippet.editorRegistry.resize.extend({
start : function () {
this._super();
- this.change_background();
- },
- onFocus: function () {
- this.$imagebg = $("
");
- this.$imagebg.prependTo(this.$target);
- this.set_options_background();
- this._super();
- },
- onBlur : function () {
- this._super();
- this.$imagebg.remove();
- },
- set_options_background: function () {
- var bg = this.$target.css("background-image").replace(/url\(|\)/g, "");
- this.$imagebg.attr("src", bg);
-
- var $ul = this.$editor.find('ul[name="parallax-background"]');
- $ul.find("li").removeClass("active");
- var selected = $ul.find('[data-value="' + bg + '"], [data-value="' + bg.replace(/.*:\/\/[^\/]+/, '') + '"]')
- .addClass('active').length;
- this.$editor.find('.oe_custom_bg b').html(selected ? "" : bg);
- },
- change_background: function () {
- var self = this;
- var $ul = this.$editor.find('ul[name="parallax-background"]');
- var $li = $ul.find("li");
-
- $li.on('click', function (event) {
- $li.removeClass("active");
- $(this).addClass("active");
- self.$editor.find('input').val("");
- })
- .on('mouseover', function (event) {
- if ($(this).data("value")) {
- var src = $(this).data("value");
- self.$imagebg.attr('src', src);
- self.$target.css("background-image", "url(" + src + ")");
- }
- })
- .on('mouseout', function (event) {
- var src = $ul.find('li.active').data("value");
- self.$imagebg.attr('src', src);
- self.$target.css("background-image", "url(" + src + ")");
- });
+ this.change_background(this.$target, 'ul[name="parallax-background"]');
},
});
diff --git a/addons/website/static/src/xml/website.snippets.xml b/addons/website/static/src/xml/website.snippets.xml
index 05146e5f34f..263c383ff4b 100644
--- a/addons/website/static/src/xml/website.snippets.xml
+++ b/addons/website/static/src/xml/website.snippets.xml
@@ -63,10 +63,10 @@
Effects
@@ -160,17 +160,14 @@
-
-
-

+
Set Your Banner Title
@@ -197,7 +194,9 @@
-
Well
+
+ Well
+
Manage your sales funnel with no effort with OpenERP CRM. Attract
leads, follow-up on phone calls and meetings. Analyse the quality
@@ -206,7 +205,10 @@
-
Quote
+
+

+
Quote
+
OpenERP provides essential platform for our project management.
@@ -217,7 +219,10 @@
-
Panel
+
+

+
Panel
+
OpenERP Features
@@ -233,13 +238,16 @@
-
Surprise!
+
+

+
Surprise!
+
-
Text-Image
-

+

+
Text-Image
@@ -268,8 +276,8 @@
-

-
Image-Text
+

+
Image-Text
@@ -297,15 +305,8 @@
-
-
- Jumbotron
-
- Jumbotron Sub-title
-
Contact us
-
-
-
+

+
Jumbotron
@@ -324,21 +325,8 @@
-
-
- Text Block
-
- Jumbotron Sub-title
-
-
- Organize projects around your own processes. Work
- on tasks and issues using the kanban view, schedule
- tasks using the gantt chart and control deadlines
- in the calendar view. Every project may have it's
- own stages allowing teams to optimize their job.
-
-
-
+

+
Text Block
@@ -370,7 +358,10 @@
-
Title
+
+

+
Title
+
@@ -385,8 +376,8 @@
-
-
Separator
+

+
Separator
@@ -394,17 +385,8 @@
-
-
- Big Picture
-

-
- OpenERP's Point of Sale introduces a super clean
- interface with no installation required.
-
-
-
-
+

+
Big Picture
@@ -436,23 +418,8 @@
-
- Three Columns
-
-
-

- Post job offers and keep track of each application received.
-
-
-

- Break down information silos. Share knowledge and best practices amongst all employees.
-
-
-

- The agenda of each employee is updated accordingly.
-
-
-
+

+
Three Columns
@@ -503,7 +470,10 @@
-
Portfolio
+
+

+
Portfolio
+
@@ -533,21 +503,8 @@
-
+

+
Image Gallery
@@ -586,7 +543,10 @@
-
Comparisons
+
+

+
Comparisons
+
@@ -688,7 +648,10 @@
-
Buttons
+
+

+
Button
+
@@ -702,7 +665,10 @@
-
FAQ
+
+

+
FAQ
+
-
References
+
+

+
References
+
@@ -796,25 +765,11 @@
-
Parallax
-
-
-
-
+
+

+
Parallax
+
+
+
diff --git a/addons/website/tests/__init__.py b/addons/website/tests/__init__.py
index 9ff1f01db62..b00c5984aa0 100644
--- a/addons/website/tests/__init__.py
+++ b/addons/website/tests/__init__.py
@@ -1,4 +1,4 @@
# -*- coding: utf-8 -*-
-import test_views
+import test_views, test_converter
-checks = [ test_views ]
+checks = [ test_views, test_converter, ]
diff --git a/addons/website/tests/test_converter.py b/addons/website/tests/test_converter.py
new file mode 100644
index 00000000000..7f042988916
--- /dev/null
+++ b/addons/website/tests/test_converter.py
@@ -0,0 +1,119 @@
+# -*- coding: utf-8 -*-
+from collections import namedtuple
+from functools import partial
+from xml.dom.minidom import getDOMImplementation
+
+from lxml import etree, html
+
+from openerp.tests import common
+from openerp.tools import qweb
+import unittest2
+
+renderer = qweb.QWebXml()
+impl = getDOMImplementation()
+document = impl.createDocument(None, None, None)
+
+Request = namedtuple('Request', 'cr uid registry')
+class RegistryProxy(object):
+ def __init__(self, func):
+ self.func = func
+ def __getitem__(self, name):
+ return self.func(name)
+
+class TestConvertBack(common.TransactionCase):
+ def setUp(self):
+ super(TestConvertBack, self).setUp()
+
+ self.Converter = self.registry('ir.fields.converter')
+
+ def field_rountrip_result(self, field, value, expected):
+ model = 'website.converter.test'
+ Model = self.registry(model)
+ id = Model.create(
+ self.cr, self.uid, {
+ field: value
+ })
+ [record] = Model.browse(self.cr, self.uid, [id])
+
+ e = document.createElement('span')
+ field_value = 'record.%s' % field
+ e.setAttribute('t-field', field_value)
+
+ rendered = renderer.render_tag_field(
+ e, {'field': field_value}, '', {
+ 'record': record,
+ 'request': Request(self.cr, self.uid, RegistryProxy( self.registry)),
+ })
+ element = html.fromstring(
+ rendered, parser=html.HTMLParser(encoding='utf-8'))
+
+ column = Model._all_columns[field].column
+
+ from_html = self.Converter.to_field(
+ self.cr, self.uid, model, column, 'html')
+
+ value_back, warnings = from_html(element)
+ self.assertEqual(warnings, [])
+
+ if isinstance(expected, str):
+ expected = expected.decode('utf-8')
+ self.assertEqual(value_back, expected)
+
+ def field_roundtrip(self, field, value):
+ self.field_rountrip_result(field, value, value)
+
+ def test_integer(self):
+ self.field_roundtrip('integer', 42)
+
+ def test_float(self):
+ self.field_roundtrip('float', 42.567890)
+
+ def test_numeric(self):
+ self.field_roundtrip('numeric', 42.77)
+
+ def test_char(self):
+ self.field_roundtrip('char', "foo bar")
+
+ self.field_roundtrip('char', "ⒸⓄⓇⒼⒺ")
+
+ def test_m2o(self):
+ Sub = self.registry('website.converter.test.sub')
+ sub = partial(Sub.create, self.cr, self.uid)
+ ids = [
+ sub({'name': "Foo"}),
+ sub({'name': "Bar"}),
+ sub({'name': "Baz"}),
+ ]
+
+ self.field_rountrip_result(
+ 'many2one',
+ ids[2],
+ ids[2])
+
+ def test_selection(self):
+ self.field_roundtrip('selection', 3)
+
+ def test_selection_str(self):
+ self.field_roundtrip('selection_str', 'B')
+
+ def test_text(self):
+ self.field_roundtrip('text', """
+ You must obey the dance commander
+ Givin' out the order for fun
+ You must obey the dance commander
+ You know that he's the only one
+ Who gives the orders here,
+ Alright
+ Who gives the orders here,
+ Alright
+
+ It would be awesome
+ If we could dance-a
+ It would be awesome, yeah
+ Let's take the chance-a
+ It would be awesome, yeah
+ Let's start the show
+ Because you never know
+ You never know
+ You never know until you go
+ """)
diff --git a/addons/website/views/views.xml b/addons/website/views/views.xml
index 43bbd918c83..53146250906 100644
--- a/addons/website/views/views.xml
+++ b/addons/website/views/views.xml
@@ -364,22 +364,37 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/addons/website/website_view.xml b/addons/website/website_view.xml
index 34b426e36cf..fa3b2e67062 100644
--- a/addons/website/website_view.xml
+++ b/addons/website/website_view.xml
@@ -57,8 +57,8 @@
-
+
diff --git a/addons/website_blog/controllers/main.py b/addons/website_blog/controllers/main.py
index 66c802e0de5..21bdcda9553 100644
--- a/addons/website_blog/controllers/main.py
+++ b/addons/website_blog/controllers/main.py
@@ -21,7 +21,7 @@
from openerp.addons.web import http
from openerp.addons.web.http import request
-from openerp.addons.website import website
+from openerp.addons.website.models import website
from openerp.tools.translate import _
from openerp.tools.safe_eval import safe_eval
@@ -29,7 +29,7 @@ import simplejson
import werkzeug
-class website_mail(http.Controller):
+class WebsiteBlog(http.Controller):
_category_post_per_page = 6
_post_comment_per_page = 6
@@ -75,7 +75,8 @@ class website_mail(http.Controller):
blog_post_obj = request.registry['blog.post']
tag_obj = request.registry['blog.tag']
category_obj = request.registry['blog.category']
- user_obj = request.registry['res.users']
+
+ current_user = request.registry['res.users'].browse(cr, uid, uid, context=context)
tag = None
category = None
@@ -103,6 +104,9 @@ class website_mail(http.Controller):
blog_posts = category.blog_post_ids
elif tag:
blog_posts = tag.blog_post_ids
+ else:
+ blog_post_ids = blog_post_obj.search(cr, uid, [], context=context)
+ blog_posts = blog_post_obj.browse(cr, uid, blog_post_ids, context=context)
if blog_posts:
pager = request.website.pager(
@@ -143,6 +147,7 @@ class website_mail(http.Controller):
'blog_posts': blog_posts,
'pager': pager,
'nav_list': nav,
+ 'current_user': current_user,
'unable_editor': post.get('unable_editor')
}
diff --git a/addons/website_blog/views/website_blog_templates.xml b/addons/website_blog/views/website_blog_templates.xml
index 81a87cb1639..2c81dea4644 100644
--- a/addons/website_blog/views/website_blog_templates.xml
+++ b/addons/website_blog/views/website_blog_templates.xml
@@ -32,16 +32,27 @@
+
hidden
+
+
+
+ In
+
+
+
-
+
+
+
+
@@ -75,9 +86,11 @@