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/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/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/js/website.editor.js b/addons/website/static/src/js/website.editor.js
index 8b15422b90d..b8140b5a1c1 100644
--- a/addons/website/static/src/js/website.editor.js
+++ b/addons/website/static/src/js/website.editor.js
@@ -911,6 +911,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..3ef98d53d2d 100644
--- a/addons/website/static/src/js/website.snippets.js
+++ b/addons/website/static/src/js/website.snippets.js
@@ -573,7 +573,6 @@
onBlurEdit : function () {},
});
-
website.snippet.editorRegistry = {};
website.snippet.Editor = openerp.Class.extend({
init: function (parent, dom) {
@@ -785,6 +784,8 @@
*/
onFocus : function () {
this.$overlay.addClass('oe_active');
+ if (this.$background_img)
+ this.$background_img.prependTo(this.$target);
},
/* onFocus
@@ -792,6 +793,75 @@
*/
onBlur : function () {
this.$overlay.removeClass('oe_active');
+ if (this.$background_img)
+ this.$background_img.detach();
+ },
+
+ /*
+ * active the cke rte editor for change image url
+ */
+ active_change_image: function ($image) {
+ var self = this;
+ // cke select image with mousedown on image
+ var $image = $($image);
+ var image = $image[0];
+ var event = document.createEvent( 'HTMLEvents' );
+ event.initEvent( 'mousedown', true, true );
+ image.dispatchEvent( event );
+
+ // hack to active editor with doubleclick on editor
+ var dblclick = null;
+ $image.on("dblclick", function (evt) {
+ evt.preventDefault();
+ dblclick = new CKEDITOR.dom.event(evt.originalEvent);
+ return;
+ });
+ var event = document.createEvent( 'HTMLEvents' );
+ event.initEvent( 'dblclick', true, true );
+ image.dispatchEvent( event );
+ },
+
+ change_background: function (bg, ul_options) {
+ var self = this;
+ var $ul = this.$editor.find(ul_options);
+ var bg_value = $(bg).css("background-image").replace(/url\(|\)/g, "");
+
+ // must add an image in the DOM for rte editor
+ this.$background_img = $("").css({position: "absolute", zIndex: -10, left:-1000000, top:-1000000});
+ this.$background_img.prependTo(this.$target);
+ this.$background_img.on("node_changed", function () {
+ $(bg).css("background-image", "url(" + $(this).attr('src') + ")");
+ });
+
+ // 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 {
+ self.active_change_image(self.$background_img);
+ }
+ })
+ .on('mouseover', function (event) {
+ if ($(this).data("value")) {
+ var src = $(this).data("value");
+ self.$background_img.attr('src', src);
+ }
+ })
+ .on('mouseout', function (event) {
+ var src = $ul.find('li.active').data("value");
+ self.$background_img.attr('src', src);
+ });
},
});
@@ -991,7 +1061,7 @@
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.change_size();
},
@@ -1022,40 +1092,9 @@
}
},
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 +1163,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 a5599a506e0..b0ee431ea5c 100644
--- a/addons/website/static/src/xml/website.snippets.xml
+++ b/addons/website/static/src/xml/website.snippets.xml
@@ -139,7 +139,7 @@