[MERGE] Merged lp:~openerp-dev/openobject-addons/trunk-website-al

bzr revid: psa@tinyerp.com-20130913070653-rjue527d6czvyy8o
This commit is contained in:
Paramjit Singh Sahota
2013-09-13 12:36:53 +05:30
48 changed files with 2724 additions and 1438 deletions
+1
View File
@@ -16,5 +16,6 @@ OpenERP Website CMS
'views/themes.xml',
'views/res_config.xml',
'website_data.xml',
'website_view.xml',
],
}
+39 -21
View File
@@ -56,9 +56,12 @@ class Website(openerp.addons.web.controllers.main.Home):
request.cr.execute('SAVEPOINT pagenew')
imd = request.registry['ir.model.data']
view = request.registry['ir.ui.view']
view_model, view_id = imd.get_object_reference(request.cr, request.uid, 'website', 'default_page')
newview_id = view.copy(request.cr, request.uid, view_id)
newview = view.browse(request.cr, request.uid, newview_id)
view_model, view_id = imd.get_object_reference(
request.cr, request.uid, 'website', 'default_page')
newview_id = view.copy(
request.cr, request.uid, view_id, context=request.context)
newview = view.browse(
request.cr, request.uid, newview_id, context=request.context)
newview.write({
'arch': newview.arch.replace("website.default_page", path),
'name': "page/%s" % path,
@@ -72,7 +75,7 @@ class Website(openerp.addons.web.controllers.main.Home):
'model': 'ir.ui.view',
'res_id': newview_id,
'noupdate': True
})
}, context=request.context)
except psycopg2.IntegrityError:
request.cr.execute('ROLLBACK TO SAVEPOINT pagenew')
else:
@@ -87,30 +90,39 @@ class Website(openerp.addons.web.controllers.main.Home):
imd = request.registry['ir.model.data']
view = request.registry['ir.ui.view']
view_model, view_option_id = imd.get_object_reference(request.cr, request.uid, 'website', 'theme')
views = view.search(request.cr, request.uid, [('inherit_id','=',view_option_id)])
view.write(request.cr, request.uid, views, {'inherit_id': False})
view_model, view_option_id = imd.get_object_reference(
request.cr, request.uid, 'website', 'theme')
views = view.search(
request.cr, request.uid, [('inherit_id', '=', view_option_id)],
context=request.context)
view.write(request.cr, request.uid, views, {'inherit_id': False},
context=request.context)
if theme_id:
module, xml_id = theme_id.split('.')
view_model, view_id = imd.get_object_reference(request.cr, request.uid, module, xml_id)
view.write(request.cr, request.uid, [view_id], {'inherit_id':view_option_id})
view_model, view_id = imd.get_object_reference(
request.cr, request.uid, module, xml_id)
view.write(request.cr, request.uid, [view_id],
{'inherit_id': view_option_id}, context=request.context)
return request.webcontext.render('website.themes', {'theme_changed': True})
return request.website.render('website.themes', {'theme_changed': True})
@website.route('/page/<path:path>', type='http', auth="admin")
def page(self, path, **kwargs):
request.webcontext['path'] = path
values = {
'path': path,
}
try:
html = request.webcontext.render(path)
html = request.website.render(path, values)
except ValueError:
html = request.webcontext.render('website.404')
html = request.website.render('website.404', values)
return html
@website.route('/website/customize_template_toggle', type='json', auth='admin') # FIXME: auth
def customize_template_set(self, view_id):
view_obj = request.registry.get("ir.ui.view")
view = view_obj.browse(request.cr, request.uid, int(view_id), context=request.context)
view = view_obj.browse(request.cr, request.uid, int(view_id),
context=request.context)
if view.inherit_id:
value = False
else:
@@ -121,19 +133,21 @@ class Website(openerp.addons.web.controllers.main.Home):
return True
@website.route('/website/customize_template_get', type='json', auth='admin') # FIXME: auth
def customize_template_get(self, xml_id):
def customize_template_get(self, xml_id, optional=True):
imd = request.registry['ir.model.data']
view_model, view_theme_id = imd.get_object_reference(request.cr, request.uid, 'website', 'theme')
view_model, view_theme_id = imd.get_object_reference(
request.cr, request.uid, 'website', 'theme')
view = request.registry.get("ir.ui.view")
views = view._views_get(request.cr, request.uid, xml_id, request.context)
done = {}
result = []
for v in views:
if v.inherit_option_id and v.inherit_option_id.id<>view_theme_id:
if v.inherit_option_id and v.inherit_option_id.id != view_theme_id or not optional:
if v.inherit_option_id.id not in done:
result.append({
'name': v.inherit_option_id.name,
'id': v.id,
'header': True,
'active': False
})
@@ -221,8 +235,10 @@ class Website(openerp.addons.web.controllers.main.Home):
width = int(resize[0])
height = int(resize[1])
# resize maximum 500*500
if width > 500: width = 500
if height > 500: height = 500
if width > 500:
width = 500
if height > 500:
height = 500
image_base64 = openerp.tools.image_resize_image(base64_source=image_base64, size=(width, height), encoding='base64', filetype='PNG')
image_data = base64.b64decode(image_base64)
@@ -244,13 +260,15 @@ class Website(openerp.addons.web.controllers.main.Home):
_object = request.registry[post['object']]
obj = _object.browse(request.cr, request.uid, _id)
_object.write(request.cr, request.uid, [_id], {'website_published': not obj.website_published})
_object.write(request.cr, request.uid, [_id],
{'website_published': not obj.website_published},
context=request.context)
obj = _object.browse(request.cr, request.uid, _id)
return obj.website_published and "1" or "0"
@website.route(['/website/kanban/'], type='http', auth="public")
def kanban(self, **post):
return request.registry['website'].kanban_col(**post)
return request.website.kanban_col(**post)
# vim:expandtab:tabstop=4:softtabstop=4:shiftwidth=4:
+183 -31
View File
@@ -21,12 +21,11 @@
position: fixed;
top: 0;
right: 0;
z-index: 10;
display: block;
width: 100%;
padding: 2px;
margin: 0px;
z-index: 10000;
margin: 0;
z-index: 20000;
background-color: #414141;
background: -webkit-linear-gradient(#646060, #262626);
box-sizing: border-box;
@@ -66,7 +65,7 @@
}
.css_non_editable_mode_hidden {
display: none
display: none;
}
.oe_carousel_options {
@@ -76,16 +75,41 @@
z-index: 1;
display: none;
}
.oe_carousel_options > * {
display: inline-block !important;
vertical-align: middle !important;
position: relative !important;
top: +2px;
top: 2px;
}
/* ---- EDITOR BAR ---- */
.modal .tab-content {
padding-top: 5px;
}
table.editorbar-panel {
cursor: pointer;
width: 100%;
}
table.editorbar-panel td.selected {
background-color: #b1c9d9;
}
.modal .image-preview-container, .modal .image-preview {
margin-bottom: 0.5em;
}
/* ---- RTE ---- */
.oe_editable .btn {
-webkit-user-select: auto;
-moz-user-select: auto;
-ms-user-select: auto;
-o-user-select: auto;
user-select: auto;
}
/* ---- SNIPPET EDITOR ---- */
.oe_snippets {
#oe_snippets {
position: fixed;
left: 0px;
right: 0px;
@@ -95,7 +119,17 @@
background: #282828;
box-shadow: 0px 10px 10px -10px black inset;
overflow-y: auto;
z-index: 100;
z-index: 20000;
}
#oe_snippets .nav-pills {
padding: 3px 8px;
background: #444444;
}
#oe_snippets .nav-pills > li > a {
padding: 2px 10px !important;
}
#oe_snippets .pill-content {
height: 140px;
}
.oe_snippet {
@@ -122,6 +156,7 @@
line-height: 120px;
color: white;
text-shadow: 0px 1px 2px rgba(0, 0, 0, 0.95);
box-shadow: 0px 0px 0px 1px rgba(255, 255, 255, 0.07) inset;
}
.oe_snippet .oe_snippet_body {
display: none;
@@ -129,6 +164,9 @@
.oe_snippet > * {
pointer-events: none;
}
.oe_snippet > :not(.oe_snippet_body):not(.oe_snippet_thumbnail) {
display: none !important;
}
.oe_snippet > *:first-child:not(.oe_snippet_thumbnail) {
display: block;
-webkit-transform-origin-x: 5px;
@@ -180,64 +218,134 @@
}
.oe_drop_zone.oe_hover {
background: rgba(0, 255, 133, 0.22);
z-index: 99;
z-index: 1001;
}
/* ---- SNIPPET MANIPULATOR ---- */
.oe_snippet_manipulator {
.oe_overlay {
display: block;
position: absolute;
background: rgba(153, 0, 255, 0.1);
background: transparent;
border-radius: 3px;
border: solid 1px rgba(153, 0, 255, 0.5);
opacity: 0;
transition: opacity 100ms linear;
box-sizing: border-box;
pointer-events: none;
}
.oe_snippet_manipulator > .oe_handle {
display: none;
.oe_overlay .oe_handle {
display: none !important;
}
.oe_snippet_manipulator:hover, .oe_snippet_manipulator.oe_hover {
.oe_overlay.oe_hover {
opacity: 1;
cursor: pointer;
}
.oe_snippet_manipulator:hover > .oe_handle, .oe_snippet_manipulator.oe_hover > .oe_handle {
.oe_overlay.oe_selected, .oe_overlay.oe_active {
opacity: 1;
background: transparent;
border-style: dashed;
border-width: 1px;
box-shadow: 0px 0px 0px 1px rgba(255, 255, 255, 0.3), 0px 0px 0px 1px rgba(255, 255, 255, 0.3) inset;
}
.oe_overlay.oe_selected {
border-color: rgba(0, 153, 255, 0.5);
}
.oe_overlay.oe_active {
border-color: rgba(153, 0, 255, 0.5);
}
.oe_overlay.oe_active .oe_handle {
display: block !important;
pointer-events: auto;
}
.oe_overlay .oe_overlay_options {
display: none;
z-index: 1002;
}
.oe_overlay .oe_overlay_options .oe_option {
top: 50%;
left: 50%;
pointer-events: auto;
position: absolute;
display: block;
z-index: 1002;
}
.oe_overlay .oe_overlay_options .oe_option.n {
top: -11px;
}
.oe_overlay .oe_overlay_options .oe_option.e {
left: auto;
right: -6px;
}
.oe_overlay .oe_overlay_options .oe_option.s {
top: auto;
bottom: -10px;
}
.oe_overlay .oe_overlay_options .oe_option.w {
left: -6px;
}
.oe_overlay .oe_overlay_options .oe_option > a {
text-decoration: none;
font-size: 1.2em;
}
.oe_overlay.oe_active .oe_overlay_options {
display: block;
}
.oe_handle {
pointer-events: auto;
position: absolute;
top: 50%;
left: 50%;
display: block;
background: white;
border: solid 1px rgba(153, 0, 255, 0.5);
box-sizing: border-box;
border-radius: 2px;
width: 16px;
height: 16px;
margin: -8px;
cursor: move;
-webkit-transition: background 100ms linear;
}
.oe_handle:hover {
background: #9900ff;
width: 8px;
height: 8px;
margin: -4px;
}
.oe_handle.nw, .oe_handle.n, .oe_handle.ne {
.oe_handle.n {
left: 2px;
width: 100%;
top: 0px;
cursor: s-resize;
}
.oe_handle.ne, .oe_handle.e, .oe_handle.se {
.oe_handle.e {
left: auto;
top: 2px;
height: 100%;
right: 0px;
cursor: w-resize;
}
.oe_handle.sw, .oe_handle.s, .oe_handle.se {
.oe_handle.s {
top: auto;
left: 2px;
width: 100%;
bottom: 0px;
cursor: n-resize;
}
.oe_handle.nw, .oe_handle.w, .oe_handle.sw {
.oe_handle.w {
top: 2px;
height: 100%;
left: 0px;
cursor: e-resize;
}
.s-resize-important, .s-resize-important * {
cursor: s-resize !important;
}
.n-resize-important, .n-resize-important * {
cursor: n-resize !important;
}
.e-resize-important, .e-resize-important * {
cursor: e-resize !important;
}
.w-resize-important, .w-resize-important * {
cursor: w-resize !important;
}
/* ---- MOBILE PREVIEW ---- */
@@ -297,3 +405,47 @@
.oe_seo_configuration .oe_seo_suggestion {
cursor: pointer;
}
/* ---- ACE EDITOR ---- */
.oe_ace_view_editor {
transition: all 0.2s ease-in;
opacity: 0.3;
position: fixed;
top: 51px;
right: 0;
z-index: 1000;
height: 100%;
}
.oe_ace_view_editor .oe_ace_view_editor_title {
width: 100%;
padding-top: 4px;
padding-left: 4px;
height: 40px;
background: #2f3129;
}
.oe_ace_view_editor .oe_ace_view_editor_title .oe_view_list {
width: 50%;
height: 32px;
}
.oe_ace_view_editor .oe_ace_view_editor_title .btn {
height: 32px;
padding: 0 4px 0 4px;
}
.oe_ace_view_editor .ace_editor {
position: absolute;
top: 40px;
right: 0;
bottom: 51px;
left: 0;
width: 720px;
}
.oe_ace_view_editor:hover {
opacity: 1;
}
.oe_ace_view_editor.oe_ace_open {
width: 720px;
}
.oe_ace_view_editor.oe_ace_closed {
width: 0;
opacity: 0;
}
+161 -30
View File
@@ -21,12 +21,11 @@
position: fixed
top: 0
right: 0
z-index: 10
display: block
width: 100%
padding: 2px
margin: 0px
z-index: 10000
margin: 0
z-index: 20000
background-color: #414141
background: -webkit-linear-gradient(#646060, #262626)
box-sizing: border-box
@@ -58,7 +57,7 @@
.oe_editable:focus
outline: none !important
.css_non_editable_mode_hidden:
.css_non_editable_mode_hidden
display: none
.oe_carousel_options
@@ -71,11 +70,36 @@
display: inline-block !important
vertical-align: middle !important
position: relative !important
top: +2px;
top: +2px
/* ---- EDITOR BAR ---- */
.modal .tab-content
padding-top: 5px
table.editorbar-panel
cursor: pointer
width: 100%
td.selected
background-color: #b1c9d9
.modal .image-preview-container, .modal .image-preview
margin-bottom: 0.5em
/* ---- RTE ---- */
// bootstrap makes .btn elements unselectable -> RTE double-click can't know
// about them either
.oe_editable .btn
-webkit-user-select: auto
-moz-user-select: auto
-ms-user-select: auto
-o-user-select: auto
user-select: auto
/* ---- SNIPPET EDITOR ---- */
.oe_snippets
#oe_snippets
position: fixed
left: 0px
right: 0px
@@ -85,7 +109,14 @@
background: rgb(40,40,40)
box-shadow: 0px 10px 10px -10px black inset
overflow-y: auto
z-index: 100
z-index: 20000
.nav-pills
padding: 3px 8px
background: #444444
> li > a
padding: 2px 10px !important
.pill-content
height: 140px
.oe_snippet
display: inline-block
@@ -111,11 +142,14 @@
line-height: 120px
color: white
text-shadow: 0px 1px 2px rgba(0, 0, 0, 0.95)
box-shadow: 0px 0px 0px 1px rgba(255, 255, 255, 0.07) inset
.oe_snippet_body
display: none
& > *
pointer-events: none
& > :not(.oe_snippet_body):not(.oe_snippet_thumbnail)
display: none !important
& > *:first-child:not(.oe_snippet_thumbnail)
display: block
@@ -161,52 +195,107 @@
border-radius: 3px
&.oe_hover
background: rgba(0, 255, 133, 0.22)
z-index: 99
z-index: 1001
/* ---- SNIPPET MANIPULATOR ---- */
.oe_snippet_manipulator
.oe_overlay
display: block
position: absolute
background: rgba(153, 0, 255, 0.1)
background: transparent //rgba(153, 0, 255, 0.05)
border-radius: 3px
border: solid 1px rgba(153, 0, 255, 0.5)
opacity: 0
transition: opacity 100ms linear
& > .oe_handle
display: none
&:hover,&.oe_hover
box-sizing: border-box
pointer-events: none
.oe_handle
display: none !important
&.oe_hover
opacity: 1
& > .oe_handle
cursor: pointer
&.oe_selected, &.oe_active
opacity: 1
background: transparent
border-style: dashed
border-width: 1px
box-shadow: 0px 0px 0px 1px rgba(255,255,255,0.3), 0px 0px 0px 1px rgba(255,255,255,0.3) inset
&.oe_selected
border-color: rgba(0, 153, 255, 0.5)
&.oe_active
border-color: rgba(153, 0, 255, 0.5)
.oe_handle
display: block !important
pointer-events: auto
.oe_overlay_options
display: none
z-index: 1002
.oe_option
top: 50%
left: 50%
pointer-events: auto
position: absolute
display: block
z-index: 1002
&.n
top: -11px
&.e
left: auto
right: -6px
&.s
top: auto
bottom: -10px
&.w
left: -6px
> a
text-decoration: none
font-size: 1.2em
&.oe_active .oe_overlay_options
display: block
.oe_handle
pointer-events: auto
position: absolute
top: 50%
left: 50%
display: block
background: rgba(255, 255, 255, 1)
border: solid 1px rgba(153, 0, 255, 0.5)
box-sizing: border-box
border-radius: 2px
width: 16px
height: 16px
margin: -8px
cursor: move
-webkit-transition: background 100ms linear
&:hover
background: rgb(153, 0, 255)
width: 8px
height: 8px
margin: -4px
.oe_handle.nw,.oe_handle.n,.oe_handle.ne
.oe_handle.n
left: 2px
width: 100%
top: 0px
.oe_handle.ne,.oe_handle.e,.oe_handle.se
cursor: s-resize
.oe_handle.e
left: auto
top: 2px
height: 100%
right: 0px
.oe_handle.sw,.oe_handle.s,.oe_handle.se
cursor: w-resize
.oe_handle.s
top: auto
left: 2px
width: 100%
bottom: 0px
.oe_handle.nw,.oe_handle.w,.oe_handle.sw
cursor: n-resize
.oe_handle.w
top: 2px
height: 100%
left: 0px
cursor: e-resize
.s-resize-important, .s-resize-important *
cursor: s-resize !important
.n-resize-important, .n-resize-important *
cursor: n-resize !important
.e-resize-important, .e-resize-important *
cursor: e-resize !important
.w-resize-important, .w-resize-important *
cursor: w-resize !important
/* ---- MOBILE PREVIEW ---- */
@@ -261,4 +350,46 @@ $remove_color: $icon_close
.oe_remove
color: $remove_color
.oe_seo_suggestion
cursor: pointer
cursor: pointer
/* ---- ACE EDITOR ---- */
$ace_width: 720px
$editorbar_height: 40px
// TODO Fix => might break with themes
$navbar_height: 51px
.oe_ace_view_editor
transition: all 0.2s ease-in
opacity: 0.3
position: fixed
top: $navbar_height
right: 0
z-index: 1000
height: 100%
.oe_ace_view_editor_title
width: 100%
padding-top: 4px
padding-left: 4px
height: $editorbar_height
background: #2F3129
.oe_view_list
width: 50%
height: $editorbar_height - 8px
.btn
height: $editorbar_height - 8px
padding: 0 4px 0 4px
.ace_editor
position: absolute
top: $editorbar_height
right: 0
bottom: $navbar_height
left: 0
width: $ace_width
&:hover
opacity: 1
&.oe_ace_open
width: $ace_width
&.oe_ace_closed
width: 0
opacity: 0
+33 -2
View File
@@ -88,7 +88,7 @@
padding: 8px;
}
.oe_snippet_editor .oe_snippet.oe_selected{
.oe_snippet_editor .oe_snippet.oe_selected, .oe_snippet_editor .oe_snippet.oe_active{
border: 2px solid rgb(151, 137, 255);
box-shadow: 0px 3px 17px rgba(99, 53, 150, 0.59);
@@ -134,7 +134,7 @@
box-shadow: 0px 1px 3px rgb(90,90,90) inset;
border: 0px;
}
.oe_snippet_editor .oe_snippet.oe_new.oe_selected{
.oe_snippet_editor .oe_snippet.oe_new.oe_selected, .oe_snippet_editor .oe_snippet.oe_new.oe_active{
box-shadow: 0px 2px 0px 0px rgb(151,137,255) inset, 0px 3px 17px rgba(99, 53, 150, 0.59) inset;
}
.oe_snippet_editor .oe_snippet.oe_new > *{
@@ -176,3 +176,34 @@
margin-left: 0px !important;
}
*/
#website-top-edit li.oe_snippet_editorbar {
padding: 1px 8px 2px;
font: normal normal normal 12px Arial,Helvetica,Tahoma,Verdana,Sans-Serif;
float: left;
margin-top: 6px;
border: 1px solid #a6a6a6;
border-bottom-color: #979797;
background: #eeeeee;
border-radius: 3px;
}
#website-top-edit li.oe_snippet_editorbar > * {
display: inline-block;
height: 22px;
padding: 4px 6px;
outline: 0;
border: 0;
}
#website-top-edit li.oe_snippet_editorbar a.button .icon {
cursor: inherit;
background-repeat: no-repeat;
margin-top: 1px;
width: 16px;
height: 16px;
display: inline-block;
}
[data-snippet-id] {
min-height: 10px;
min-width: 10px;
}
+3 -4
View File
@@ -127,7 +127,7 @@ footer {
margin-bottom: 0px;
}
#myCarousel .item {
.carousel .item {
color: white;
}
@@ -193,6 +193,8 @@ a[data-publish][data-publish='on']:hover .css_published {
[data-publish]:not(a) {
position: relative;
overflow: visible;
/*&:hover > [data-publish] */
/* display: block */
}
[data-publish]:not(a) > [data-publish] {
position: absolute;
@@ -200,6 +202,3 @@ a[data-publish][data-publish='on']:hover .css_published {
top: -10px;
display: none;
}
[data-publish]:not(a):hover > [data-publish] {
display: block;
}
+3 -3
View File
@@ -95,7 +95,7 @@ footer
// .nav > li a
// display: block
#myCarousel .item
.carousel .item
color: white
/* ---- HOMEPAGE THEME CUSTOMIZATION ---- */
@@ -156,5 +156,5 @@ a[data-publish]
right: -6px
top: -10px
display: none
&:hover > [data-publish]
display: block
/*&:hover > [data-publish]*/
/* display: block*/
+148
View File
@@ -0,0 +1,148 @@
(function () {
'use strict';
var globalEditor;
var website = openerp.website;
website.templates.push('/website/static/src/xml/website.ace.xml');
website.EditorBar.include({
events: _.extend({}, website.EditorBar.prototype.events, {
'click a[data-action=ace]': 'launchAce',
}),
launchAce: function () {
if (globalEditor) {
globalEditor.open();
} else {
globalEditor = new website.ace.ViewEditor(this);
globalEditor.appendTo($(document.body));
}
},
});
website.ace = {};
website.ace.XmlDocument = openerp.Class.extend({
init: function (text) {
this.xml = text;
},
isWellFormed: function () {
if (document.implementation.createDocument) {
var dom = new DOMParser().parseFromString(this.xml, "text/xml");
return dom.getElementsByTagName("parsererror").length === 0;
} else if (window.ActiveXObject) {
// TODO test in IE
var msDom = new ActiveXObject("Microsoft.XMLDOM");
msDom.async = false;
return !msDom.loadXML(this.xml);
}
return true;
},
format: function () {
return vkbeautify.xml(this.xml, 4);
},
});
website.ace.ViewOption = openerp.Widget.extend({
template: 'website.ace_view_option',
init: function (parent, options) {
this.view_id = options.id;
this.view_name = options.name;
this._super(parent);
},
});
website.ace.ViewEditor = openerp.Widget.extend({
template: 'website.ace_view_editor',
events: {
'change #ace-view-list': 'displayView',
'click button[data-action=save]': 'saveView',
'click button[data-action=format]': 'formatXml',
'click button[data-action=close]': 'close',
},
start: function () {
var self = this;
var viewId = $(document.documentElement).data('view-xmlid');
openerp.jsonRpc('/website/customize_template_get', 'call', {
'xml_id': viewId,
'optional': false,
}).then(function (views) {
self.loadViewList.call(self, views);
});
this.$el.hover();
},
selectedViewId: function () {
return parseInt(this.$('#ace-view-list').val(), 10);
},
loadViewList: function (views) {
var activeViews = _.filter(views, function (view) {
return view.active;
});
var $viewList = this.$('#ace-view-list');
_.each(activeViews, function (view) {
if (view.id) {
new website.ace.ViewOption(this, view).appendTo($viewList);
}
});
var editor = ace.edit(this.$('#ace-view-editor')[0]);
editor.setTheme("ace/theme/monokai");
this.aceEditor = editor;
this.open();
},
displayView: function () {
var editor = this.aceEditor;
openerp.jsonRpc('/web/dataset/call', 'call', {
model: 'ir.ui.view',
method: 'read',
args: [[this.selectedViewId()], ['arch'], website.get_context()],
}).then(function(result) {
var xml = new website.ace.XmlDocument(result[0].arch);
var editingSession = new ace.EditSession(xml.xml);
editingSession.setMode("ace/mode/xml");
editingSession.setUndoManager(new ace.UndoManager());
editor.setSession(editingSession);
});
},
formatXml: function () {
var xml = new website.ace.XmlDocument(this.aceEditor.getValue());
this.aceEditor.setValue(xml.format());
},
saveView: function () {
var self = this;
var xml = new website.ace.XmlDocument(this.aceEditor.getValue());
if (xml.isWellFormed()) {
openerp.jsonRpc('/web/dataset/call', 'call', {
model: 'ir.ui.view',
method: 'write',
args: [[this.selectedViewId()], { 'arch': xml.xml }, website.get_context()],
}).then(function(result) {
self.reloadPage();
}).fail(function (error) {
self.displayError(error);
});
} else {
self.displayError();
}
},
reloadPage: function () {
// TODO Reload { header + div#wrap + footer } only
window.location.reload();
},
displayError: function (error) {
// TODO Improve feedback (e.g. update 'Save' button + tooltip)
alert("Malformed XML document");
},
open: function () {
this.$el.removeClass('oe_ace_closed').addClass('oe_ace_open');
this.displayView();
},
close: function () {
var self = this;
this.$el.bind('transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd', function () {
globalEditor = null;
self.destroy.call(self);
}).removeClass('oe_ace_open').addClass('oe_ace_closed');
},
});
})();
+133 -64
View File
@@ -53,12 +53,76 @@
canUndo: false,
editorFocus: true,
});
}
});
CKEDITOR.plugins.add( 'tablebutton', {
requires: 'panelbutton,floatpanel',
init: function( editor ) {
var label = "Table";
editor.ui.add('TableButton', CKEDITOR.UI_PANELBUTTON, {
label: label,
title: label,
// use existing 'table' icon
icon: 'table',
modes: { wysiwyg: true },
editorFocus: true,
// panel opens in iframe, @css is CSS file <link>-ed within
// frame document, @attributes are set on iframe itself.
panel: {
css: '/website/static/src/css/editor.css',
attributes: { 'role': 'listbox', 'aria-label': label, },
},
onBlock: function (panel, block) {
block.autoSize = true;
block.element.setHtml(openerp.qweb.render('website.editor.table.panel', {
rows: 5,
cols: 5,
}));
var $table = $(block.element.$).on('mouseenter', 'td', function (e) {
var $e = $(e.target);
var y = $e.index() + 1;
var x = $e.closest('tr').index() + 1;
$table
.find('td').removeClass('selected').end()
.find('tr:lt(' + String(x) + ')')
.children().filter(function () { return $(this).index() < y; })
.addClass('selected');
}).on('click', 'td', function (e) {
var $e = $(e.target);
var table = new CKEDITOR.dom.element(
$(openerp.qweb.render('website.editor.table', {
rows: $e.closest('tr').index() + 1,
cols: $e.index() + 1,
}))[0]);
editor.insertElement(table);
setTimeout(function () {
var firstCell = new CKEDITOR.dom.element(table.$.rows[0].cells[0]);
var range = editor.createRange();
range.moveToPosition(firstCell, CKEDITOR.POSITION_AFTER_START);
range.select();
}, 0);
});
block.element.getDocument().getBody().setStyle('overflow', 'hidden');
CKEDITOR.ui.fire('ready', this);
},
});
}
});
var editor = new website.EditorBar();
var $body = $(document.body);
editor.prependTo($body);
editor.prependTo($body).then(function () {
if (location.search.indexOf("unable_editor") >= 0) {
editor.edit();
}
});
$body.css('padding-top', '50px'); // Not working properly: editor.$el.outerHeight());
};
/* ----- TOP EDITOR BAR FOR ADMIN ---- */
@@ -88,10 +152,11 @@
});
// Adding Static Menus
menu.append('<li class="divider"></li><li><a href="/page/website.themes">Change Theme</a></li>');
menu.append('<li class="divider"></li><li><a data-action="ace" href="#">Advanced view editor</a></li>');
}
);
});
menu.on('click', 'a', function (event) {
menu.on('click', 'a[data-action!=ace]', function (event) {
var view_id = $(event.currentTarget).data('view-id');
openerp.jsonRpc('/website/customize_template_toggle', 'call', {
'view_id': view_id
@@ -132,11 +197,6 @@
this.$('#website-top-edit').show();
$('.css_non_editable_mode_hidden').removeClass("css_non_editable_mode_hidden");
// this.$buttons.cancel.add(this.$buttons.snippet).prop('disabled', false)
// .add(this.$buttons.save)
// .parent().show();
//
// TODO: span edition changing edition state (save button)
var $editables = $('[data-oe-model]')
.not('link, script')
// FIXME: propagation should make "meta" blocks non-editable in the first place...
@@ -185,7 +245,7 @@
defs.push(def);
});
return $.when.apply(null, defs).then(function () {
window.location.reload();
window.location.href = window.location.href.replace(/unable_editor(=[^&]*)?|#.*/g, '');
});
},
saveElement: function ($el) {
@@ -204,11 +264,11 @@
return openerp.jsonRpc('/web/dataset/call', 'call', {
model: 'ir.ui.view',
method: 'save',
args: [data.oeModel, data.oeId, data.oeField, html, xpath]
args: [data.oeModel, data.oeId, data.oeField, html, xpath, website.get_context()]
});
},
cancel: function () {
window.location.reload();
window.location.href = window.location.href.replace(/unable_editor(=[^&]*)?|#.*/g, '');
},
});
@@ -220,6 +280,11 @@
// editor.ui.items -> possible commands &al
// editor.applyStyle(new CKEDITOR.style({element: "span",styles: {color: "#(color)"},overrides: [{element: "font",attributes: {color: null}}]}, {color: '#ff0000'}));
init: function (EditorBar) {
this.EditorBar = EditorBar;
this._super.apply(this, arguments);
},
start_edition: function ($elements) {
var self = this;
$elements
@@ -229,6 +294,7 @@
var $node = $(node);
var editor = CKEDITOR.inline(this, self._config());
editor.on('instanceReady', function () {
self.trigger('instanceReady');
observer.observe(node, OBSERVER_CONFIG);
});
$node.one('content_changed', function () {
@@ -262,11 +328,14 @@
autoParagraph: false,
filebrowserImageUploadUrl: "/website/attach",
// Support for sharedSpaces in 4.x
extraPlugins: 'sharedspace,customdialogs',
extraPlugins: 'sharedspace,customdialogs,tablebutton',
// Place toolbar in controlled location
sharedSpaces: { top: 'oe_rte_toolbar' },
toolbar: [
{name: 'basicstyles', items: [
toolbar: [{
name: 'clipboard', items: [
"Undo"
]},{
name: 'basicstyles', items: [
"Bold", "Italic", "Underline", "Strike", "Subscript",
"Superscript", "TextColor", "BGColor", "RemoveFormat"
]},{
@@ -278,7 +347,7 @@
"JustifyLeft", "JustifyCenter", "JustifyRight", "JustifyBlock"
]},{
name: 'special', items: [
"Image", "Table"
"Image", "TableButton"
]},{
name: 'styles', items: [
"Styles"
@@ -330,15 +399,11 @@
website.editor.LinkDialog = website.editor.Dialog.extend({
template: 'website.editor.dialog.link',
events: _.extend({}, website.editor.Dialog.prototype.events, {
'change .url-source': function (e) { this.changed($(e.target)); },
'click div.existing a': 'select_page',
}),
init: function (editor) {
this._super(editor);
// url -> name mapping for existing pages
this.pages = Object.create(null);
// name -> url mapping for the same
this.pages_by_name = Object.create(null);
},
start: function () {
var element;
@@ -397,24 +462,24 @@
},
save: function () {
var self = this, _super = this._super.bind(this);
var $e = this.$('.url-source').filter(function () { return !!this.value; });
var $active_tab = this.$('.tab-pane.active');
var $e = $active_tab.find('.url-source');
var val = $e.val(), done = $.when();
if ($e.hasClass('email-address')) {
if ($active_tab.is('#link-email')) {
this.make_link('mailto:' + val, false, val);
} else if ($e.hasClass('pages')) {
// ``val`` is the *name* of the page
var url = this.pages_by_name[val];
if (!url) {
// Create the page, get the URL back
done = $.get(_.str.sprintf(
'/pagenew/%s?noredirect', encodeURIComponent(val)))
.then(function (response) {
url = response;
});
}
} else if ($active_tab.is('#link-existing')) {
self.make_link(val, false, this.pages[val]);
} else if ($active_tab.is('#link-new')) {
// Create the page, get the URL back
done = $.get(_.str.sprintf(
'/pagenew/%s?noredirect', encodeURIComponent(val)))
.then(function (response) {
val = response;
});
done.then(function () {
self.make_link(url, false, val);
self.make_link(val, false);
});
} else {
this.make_link(val, this.$('input.window-new').prop('checked'));
@@ -428,36 +493,19 @@
var match, $control;
if (match = /(mailto):(.+)/.exec(href)) {
$control = this.$('input.email-address').val(match[2]);
$control = this.$('#link-email input').val(match[2]);
} else if(href in this.pages) {
$control = this.$('input.pages').val(this.pages[href]);
}
if (!$control) {
$control = this.$('input.url').val(href);
$control = this.$('#link-existing select').val(href);
} else {
$control = this.$('#link-external input:first').val(href);
}
this.changed($control);
var tab_name = $control.closest('.tab-pane').attr('id');
this.$('.nav a[href="#' + tab_name + '"]').tab('show');
this.$('input.window-new').prop(
'checked', this.element.getAttribute('target') === '_blank');
},
changed: function ($e) {
$e.closest('li.list-group-item').addClass('active')
.siblings().removeClass('active');
this.$('.url-source').not($e).val('');
},
/**
* Selected an existing page in dropdown
*/
select_page: function (e) {
e.preventDefault();
e.stopPropagation();
var $target = $(e.target);
this.$('input.pages').val($target.text()).change();
// No #dropdown('close'), and using #dropdown('toggle') sur
// #closest('.dropdown') makes the dropdown not work correctly
$target.closest('.open').removeClass('open')
},
/**
* CKEDITOR.plugins.link.getSelectedLink ignores the editor's root,
* if the editor is set directly on a link it will thus not work.
@@ -480,37 +528,50 @@
model: 'website',
method: 'list_pages',
args: [],
kwargs: {}
kwargs: {
context: website.get_context()
},
});
},
fill_pages: function (results) {
var self = this;
var $pages = this.$('div.existing ul').empty();
var pages = this.$('#link-existing-select')[0];
_(results).each(function (result) {
self.pages[result.url] = result.name;
self.pages_by_name[result.name] = result.url;
var $link = $('<a>').attr('href', result.url).text(result.name);
$('<li>').append($link).appendTo($pages);
pages.options[pages.options.length] =
new Option(result.name, result.url);
});
},
});
website.editor.ImageDialog = website.editor.Dialog.extend({
template: 'website.editor.dialog.image',
events: _.extend({}, website.editor.Dialog.prototype.events, {
'change .url-source': function (e) { this.changed($(e.target)); },
'click button.filepicker': function () {
this.$('input[type=file]').click();
},
'change input[type=file]': 'file_selection',
'change input.url': 'preview_image',
'change select.image-style': 'preview_image',
'click .existing-attachments a': 'select_existing',
}),
start: function () {
var selection = this.editor.getSelection();
var el = selection && selection.getSelectedElement();
this.element = null;
var $select = this.$('.image-style');
var $options = $select.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 should follow setup of image style
this.set_image(el.getAttribute('src'));
}
@@ -520,6 +581,7 @@
},
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');
@@ -533,7 +595,10 @@
}, 0);
}
element.setAttribute('src', url);
this._super();
$(element.$).removeClass(this.image_styles.join(' '));
if (style) { element.addClass(style); }
return this._super();
},
/**
@@ -571,7 +636,10 @@
var image = this.$('input.url').val();
if (!image) { return; }
this.$('img.image-preview').attr('src', image);
this.$('img.image-preview')
.attr('src', image)
.removeClass(this.image_styles.join(' '))
.addClass(this.$('select.image-style').val());
},
fetch_existing: function () {
@@ -584,6 +652,7 @@
fields: ['name'],
domain: [['res_model', '=', 'ir.ui.view']],
order: 'name',
context: website.get_context(),
}
});
},
+17 -39
View File
@@ -9,6 +9,13 @@
'/website/static/src/xml/website.xml'
];
website.get_context = function (dict) {
var html = document.documentElement;
return _.extend({
lang: html.getAttribute('lang').replace('-', '_')
}, dict);
};
/* ----- TEMPLATE LOADING ---- */
website.add_template = function(template) {
templates.push(template);
@@ -16,48 +23,19 @@
website.load_templates = function(templates) {
var def = $.Deferred();
var count = templates.length;
templates.forEach(function(t) {
openerp.qweb.add_template(t, function(err) {
if (err) {
def.reject();
} else {
count--;
if (count < 1) {
def.resolve();
var dones = _(templates).map(function (t) {
return new $.Deferred(function (d) {
openerp.qweb.add_template(t, function(err) {
if (err) {
d.reject(err);
} else {
d.resolve();
}
}
});
});
});
return def;
};
website.mutations = {
darken: function($el){
var $parent = $el.parent();
if($parent.hasClass('dark')){
$parent.replaceWith($el);
}else{
$el.replaceWith($("<div class='dark'></div>").append($el.clone()));
}
},
vomify: function($el){
var hue=0;
var beat = false;
var a = setInterval(function(){
$el.css({'-webkit-filter':'hue-rotate('+hue+'deg)'}); hue += 5;
}, 10);
setTimeout(function(){
clearInterval(a);
setInterval(function(){
var filter = 'hue-rotate('+hue+'deg)'+ (beat ? ' invert()' : '');
$(document.documentElement).css({'-webkit-filter': filter}); hue += 5;
if(hue % 35 === 0){
beat = !beat;
}
}, 10);
},5000);
$('<iframe width="1px" height="1px" src="http://www.youtube.com/embed/WY24YNsOefk?autoplay=1" frameborder="0"></iframe>').appendTo($el);
},
return $.when.apply(null, dones);
};
+174 -172
View File
@@ -1,8 +1,9 @@
(function () {
'use strict';
// TODO l10n ?
var website = openerp.website;
website.templates.push('/website/static/src/xml/website.seo.xml');
website.EditorBar.include({
events: _.extend({}, website.EditorBar.prototype.events, {
'click a[data-action=promote-current-page]': 'promotePage',
@@ -14,6 +15,154 @@
website.seo = {};
website.seo.Suggestion = openerp.Widget.extend({
template: 'website.seo_suggestion',
events: {
'click .js_seo_suggestion': 'select',
},
init: function (parent, keyword) {
this.keyword = keyword;
// cf. http://getbootstrap.com/components/#labels
// default, primary, success, info, warning, danger
this.type = 'default';
this._super(parent);
},
select: function () {
this.trigger('selected', this.keyword);
},
});
website.seo.SuggestionList = openerp.Widget.extend({
template: 'website.seo_list',
init: function (parent, word) {
this.word = word;
this._super(parent);
},
start: function () {
this.refresh();
},
refresh: function () {
var self = this;
self.$el.append("Loading...");
function addSuggestions (list) {
self.$el.empty();
// TODO Improve algorithm + Ajust based on custom user keywords
var nameRegex = new RegExp(self.companyName, "gi");
var cleanList = _.map(list, function removeCompanyName (word) {
return word.replace(nameRegex, "").trim();
});
// TODO Order properly ?
_.each(_.uniq(cleanList), function (keyword) {
if (keyword) {
var suggestion = new website.seo.Suggestion(self, keyword);
suggestion.on('selected', self, function (word) {
self.trigger('selected', word);
});
suggestion.appendTo(self.$el);
}
});
}
$.getJSON("http://seo.eu01.aws.af.cm/suggest/"+encodeURIComponent(this.word + " "), addSuggestions);
},
});
website.seo.Keyword = openerp.Widget.extend({
template: 'website.seo_keyword',
events: {
'click a[data-action=remove-keyword]': 'destroy',
},
maxWordsPerKeyword: 4, // TODO Check
init: function (parent, keyword) {
this.keyword = keyword;
// cf. http://getbootstrap.com/components/#labels
// default, primary, success, info, warning, danger
this.type = 'warning';
this._super(parent);
},
start: function () {
var self = this;
self.suggestionList = new website.seo.SuggestionList(self, this.keyword);
self.suggestionList.on('selected', self, function (word) {
self.trigger('selected', word);
});
this.suggestionList.appendTo(this.$('.js_seo_keyword_suggestion'));
},
destroy: function () {
this.trigger('removed');
this._super();
},
});
website.seo.KeywordList = openerp.Widget.extend({
template: 'website.seo_list',
maxKeywords: 10,
keywords: function () {
var result = [];
this.$('span.js_seo_keyword').each(function () {
result.push($(this).data('keyword'));
});
return result;
},
isKeywordListFull: function () {
return this.keywords().length >= this.maxKeywords;
},
isExistingKeyword: function (word) {
return _.contains(this.keywords(), word);
},
add: function (candidate) {
var self = this;
// TODO Refine
var word = candidate ? candidate.replace(/[,;.:<>]+/g, " ").replace(/ +/g, " ").trim() : "";
if (word && !self.isKeywordListFull() && !self.isExistingKeyword(word)) {
var keyword = new website.seo.Keyword(self, word);
keyword.on('removed', self, function () {
self.trigger('list-not-full');
self.trigger('removed', word);
});
keyword.on('selected', self, function (word) {
self.trigger('selected', word);
});
keyword.appendTo(self.$el);
}
if (self.isKeywordListFull()) {
self.trigger('list-full');
}
},
});
website.seo.Image = openerp.Widget.extend({
template: 'website.seo_image',
init: function (parent, options) {
this.src = options.src;
this.alt = options.alt;
this._super(parent);
},
});
website.seo.ImageList = openerp.Widget.extend({
start: function () {
var self = this;
new website.seo.PageParser().images().each(function (index, image) {
new website.seo.Image(self, image).appendTo(self.$el);
});
},
images: function () {
var result = [];
this.$('input').each(function () {
var $input = $(this);
result.push({
src: $input.attr('src'),
alt: $input.val(),
});
});
return result;
},
add: function (image) {
new website.seo.Image(this, image).appendTo(this.$el);
},
});
website.seo.PageParser = openerp.Class.extend({
url: function () {
var url = window.location.href;
@@ -24,8 +173,8 @@
return $(document.title).text();
},
headers: function (tag) {
return $('#wrap h1').map(function () {
return $(this).text();
return $('#wrap '+tag).map(function () {
return $(this).text();
});
},
images: function () {
@@ -56,129 +205,6 @@
},
});
website.seo.Keyword = openerp.Widget.extend({
template: 'website.seo_keyword',
events: {
'click a[data-action=remove-keyword]': 'destroy',
},
maxWordsPerKeyword: 4, // TODO Check
types: {
// cf. http://getbootstrap.com/components/#labels
// default, primary, success, info, warning, danger
perfect: 'success',
advised: 'primary',
used: 'warning',
new: 'default',
},
init: function (parent, options) {
this.keyword = options.keyword;
this.type = this.types[options.type || 'default'];
this._super(parent);
},
destroy: function () {
this.trigger('removed');
this._super();
},
});
website.seo.KeywordList = openerp.Widget.extend({
template: 'website.seo_list',
maxKeywords: 10,
keywords: function () {
var result = [];
this.$el.find('.js_seo_keyword').each(function () {
result.push($(this).data('keyword'));
});
return result;
},
suggestions: function () {
// TODO Refactor (Ugly)
return $('.js_seo_suggestion').map(function () {
return $(this).data('keyword');
});
},
isKeywordListFull: function () {
return this.keywords().length >= this.maxKeywords;
},
isExistingKeyword: function (word) {
return _.contains(this.keywords(), word);
},
add: function (candidate) {
var self = this;
// TODO Refine
var word = candidate ? candidate.replace(/[,;.:<>]+/g, " ").replace(/ +/g, " ").trim() : "";
if (word && !this.isKeywordListFull() && !this.isExistingKeyword(word)) {
var type = _.contains(this.suggestions(), word) ? 'advised' : 'new';
var keyword = new website.seo.Keyword(this, {
keyword: word,
type: type,
});
keyword.on('removed', null, function () {
self.trigger('list-not-full');
});
keyword.appendTo(this.$el);
}
if (this.isKeywordListFull()) {
self.trigger('list-full');
}
},
});
website.seo.Suggestion = openerp.Widget.extend({
template: 'website.seo_suggestion',
events: {
'click .js_seo_suggestion': 'select'
},
types: {
// cf. http://getbootstrap.com/components/#labels
// default, primary, success, info, warning, danger
primary: 'primary',
secondary: 'info',
},
init: function (parent, options) {
this.keyword = options.keyword;
this.type = this.types[options.type || 'primary'];
this._super(parent);
},
select: function () {
this.trigger('selected');
},
});
website.seo.Image = openerp.Widget.extend({
template: 'website.seo_image',
init: function (parent, options) {
this.src = options.src;
this.alt = options.alt;
this._super(parent);
},
});
website.seo.ImageList = openerp.Widget.extend({
start: function () {
var self = this;
new website.seo.PageParser().images().each(function (index, image) {
new website.seo.Image(self, image).appendTo(self.$el);
});
},
images: function () {
var result = [];
this.$el.find('input').each(function () {
var $input = $(this);
result.push({
src: $input.attr('src'),
alt: $input.val(),
});
});
return result;
},
add: function (image) {
new website.seo.Image(this, image).appendTo(this.$el);
},
});
website.seo.Configurator = openerp.Widget.extend({
template: 'website.seo_configuration',
events: {
@@ -190,42 +216,47 @@
maxTitleSize: 65,
maxDescriptionSize: 155,
start: function () {
var $modal = this.$el;
var self = this;
var $modal = self.$el;
var pageParser = new website.seo.PageParser();
$modal.find('.js_seo_page_url').text(pageParser.url());
$modal.find('input[name=seo_page_title]').val(pageParser.title());
this.suggestImprovements(pageParser);
this.displayKeywordSuggestions(pageParser);
this.imageList = new website.seo.ImageList(this);
this.imageList.appendTo($modal.find('.js_seo_image_list'));
this.keywordList = new website.seo.KeywordList(this);
this.keywordList.appendTo($modal.find('.js_seo_keywords_list'));
this.keywordList.on('list-full', null, function () {
self.suggestImprovements(pageParser);
self.imageList = new website.seo.ImageList(self);
self.imageList.appendTo($modal.find('.js_seo_image_list'));
self.keywordList = new website.seo.KeywordList(self);
self.keywordList.on('list-full', self, function () {
$modal.find('input[name=seo_page_keywords]')
.attr('readonly', "readonly")
.attr('placeholder', "Remove a keyword first");
$modal.find('button[data-action=add]')
.prop('disabled', true).addClass('disabled');
});
this.keywordList.on('list-not-full', null, function () {
self.keywordList.on('list-not-full', self, function () {
$modal.find('input[name=seo_page_keywords]')
.removeAttr('readonly').attr('placeholder', "");
$modal.find('button[data-action=add]')
.prop('disabled', false).removeClass('disabled');
});
self.keywordList.on('selected', self, function (word) {
self.keywordList.add(word);
});
self.keywordList.appendTo($modal.find('.js_seo_keywords_list'));
var companyName = pageParser.company().toLowerCase();
self.addKeyword(companyName);
$modal.modal();
},
suggestImprovements: function (parser) {
var tips = [];
var self = this;
function displayTip(message, type) {
new website.seo.Tip(this, {
new website.seo.Tip(self, {
message: message,
type: type
}).appendTo(self.$el.find('.js_seo_tips'));
type: type,
}).appendTo(self.$('.js_seo_tips'));
}
var pageParser = parser || new website.seo.PageParser();
if (!pageParser.headers('h1').length === 0) {
if (pageParser.headers('h1').length === 0) {
tips.push({
type: 'warning',
message: "You don't have an &lt;h1&gt; tag on your page.",
@@ -245,50 +276,21 @@
displayTip("Your page makup is appropriate for search engines.", 'success');
}
},
displayKeywordSuggestions: function (pageParser) {
var $modal = this.$el;
var self = this;
$modal.find('.js_seo_company_suggestions').append("Loading...");
var companyName = pageParser.company().toLowerCase();
// cf. https://github.com/ddm/seo
// TODO Try with /recommend/
$.getJSON("http://seo.eu01.aws.af.cm/suggest/"+encodeURIComponent(companyName), function (list) {
$modal.find('.js_seo_company_suggestions').empty();
// TODO Improve algorithm + Ajust based on custom user keywords
var nameRegex = new RegExp(companyName, "gi");
var cleanList = _.map(list, function removeCompanyName (word) {
return word.replace(nameRegex, "").trim();
});
// TODO Order properly ?
cleanList.push(companyName);
_.each(_.uniq(cleanList), function (keyword) {
if (keyword) {
var suggestion = new website.seo.Suggestion(self, {
keyword: keyword
});
suggestion.on('selected', self, function () {
self.addKeyword(keyword);
});
suggestion.appendTo($modal.find('.js_seo_company_suggestions'));
}
});
});
},
confirmKeyword: function (e) {
if (e.keyCode == 13) {
this.addKeyword(this.$el.find('input[name=seo_page_keywords]').val());
this.addKeyword();
}
},
addKeyword: function (word) {
var $input = this.$el.find('input[name=seo_page_keywords]');
var $input = this.$('input[name=seo_page_keywords]');
var keyword = _.isString(word) ? word : $input.val();
this.keywordList.add(keyword);
$input.val("");
},
update: function () {
var data = {
title: this.$el.find('input[name=seo_page_title]').val(),
description: this.$el.find('input[name=seo_page_title]').val(),
title: this.$('input[name=seo_page_title]').val(),
description: this.$('input[name=seo_page_title]').val(),
keywords: this.keywordList.keywords(),
images: this.imageList.images(),
};
+666 -195
View File
@@ -2,99 +2,161 @@
'use strict';
var website = openerp.website;
website.templates.push('/website/static/src/xml/website.snippets.xml');
website.EditorBar.include({
events: _.extend({}, website.EditorBar.prototype.events, {
'click button[data-action=snippet]': 'snippet',
}),
start: function () {
return this._super().then(function () {
this.$buttons.snippet = this.$('button[data-action=snippet]');
window.snippets = this.snippets = new website.Snippets();
this.snippets.appendTo($(document.body));
}.bind(this));
},
edit: function () {
window.snippets = this.snippets = new website.snippet.BuildingBlock(this);
this.snippets.appendTo($(document.body));
return this._super.apply(this, arguments);
},
snippet: function (ev) {
this.snippets.toggle();
},
});
website.RTE.include({
start_edition: function ($elements) {
this.snippet_carousel();
return this._super($elements);
},
// TODO clean
snippet_carousel: function () {
var self = this;
$('.carousel').each(function () {
var $carousel = $(this);
var $options = $('.js_carousel_options', $carousel);
$options.on('click', '.label', function (e) {
e.preventDefault();
var $button = $(e.currentTarget);
var $c = $button.parents(".carousel:first");
var $carousel_inner = $c.find('.carousel-inner');
if($button.hasClass("js_add")) {
var cycle = $carousel_inner.find('.item').size();
$carousel_inner.append(openerp.qweb.render('website.carousel'));
$c.carousel(cycle);
}
else if ($carousel_inner.find('.item').size() > 1) {
$carousel_inner
.find('.item.active').remove().end()
.find('.item:first').addClass('active');
$c.carousel(0);
self.trigger('change', self, null);
}
});
$options.on('change', 'select[name="carousel-background"]', function () {
$('.carousel-inner .item.active', $carousel).css('background-image', 'url(' + $(this).val() + ')');
$(this).val("");
});
$options.on('change', 'select[name="carousel-style"]', function () {
var $container = $('.carousel-inner .item.active .container', $carousel);
$('.content_image', $container).remove();
switch ($(this).val()) {
case 'no_image':
$('.content', $container).attr("class", "content");
break;
case 'image_left':
$('.content', $container).attr("class", "content col-md-6")
.before('<div class="content_image col-md-5"><img class="img-rounded img-responsive" src="/website/static/src/img/china.jpg"></div>');
break;
case 'image_right':
$('.content', $container).attr("class", "content col-md-6")
.after('<div class="content_image col-md-5 col-lg-offset-1"><img class="img-rounded img-responsive" src="/website/static/src/img/china.jpg"></div>');
break;
}
$(this).val("");
});
$options.show();
});
save: function () {
$('body').trigger("save");
this._super();
},
});
/* ----- SNIPPET SELECTOR ---- */
website.Snippets = openerp.Widget.extend({
website.snippet = {};
// puts $el at the same absolute position as $target
website.snippet.cover_target = function ($el, $target){
$el.css({
'position': 'absolute',
'width': $target.outerWidth(),
'height': $target.outerHeight(),
});
$el.css($target.offset());
};
function hack_to_add_snippet_id () {
_.each(website.snippet.selector, function (val) {
$(val[0]).each(function() {
if ($(this).is("[data-snippet-id='"+ val[1]+"']"))
$(this).removeAttr("data-snippet-id");
if (!$(this).is("[data-snippet-id]") && $(this).parents("[data-oe-model]").length)
$(this).attr("data-snippet-id", val[1]);
});
});
}
website.snippet.selector = [];
website.snippet.BuildingBlock = openerp.Widget.extend({
template: 'website.snippets',
init: function () {
activeSnippets: [],
init: function (parent) {
this.parent = parent;
this._super.apply(this, arguments);
if(!$('#oe_manipulators').length){
$("<div id='oe_manipulators'></div>").appendTo('body');
}
this.$active_snipped_id = false;
this.active = false;
this.parent_of_editable_box = "body > :not(:has(#website-top-view)):not(#oe_manipulators):not(#oe_snippets) ";
hack_to_add_snippet_id();
},
start: function() {
var self = this;
$.ajax({
type: "GET",
url: "/page/website.snippets",
dataType: "text",
success: function(snippets){
self.$el.html(snippets);
self.start_snippets();
},
var snippets_template = [];
_.each(openerp.qweb.compiled_templates, function (val, key) {
if (key.indexOf('website.snippets.') === 0) {
var $snippet = $(openerp.qweb.render(key)).addClass("oe_snippet");
if ($snippet.data("action")) {
self.$el.find('#snippet_' + $snippet.data('category')).append($snippet);
self.make_snippet_draggable($snippet);
}
}
});
this.bind_selected_manipulator();
this.bind_snippet_click_editor();
},
bind_snippet_click_editor: function () {
var self = this;
var snipped_event_flag = false;
$("body").on('click', "[data-snippet-id]", function (event) {
if (!self.active || snipped_event_flag) {
return;
}
var $target = $(event.currentTarget);
if (self.$active_snipped_id) {
if (self.$active_snipped_id[0] === $target[0] || $.contains(self.$active_snipped_id, $target[0])) {
var $parent = self.$active_snipped_id.parents("[data-snippet-id]:first");
if ($parent.length) {
$target = $parent;
}
}
}
snipped_event_flag = true;
setTimeout(function () {snipped_event_flag = false;}, 0);
self.make_active($target);
});
$("body > :not(:has(#website-top-view)):not(#oe_manipulators)").on('click', function (ev) {
if (!snipped_event_flag && self.$active_snipped_id && !self.$active_snipped_id.parents("[data-snippet-id]:first")) {
self.make_active(false);
}
});
},
snippet_blur: function ($snipped_id) {
if ($snipped_id) {
if ($snipped_id.data("snippet-editor")) {
$snipped_id.data("snippet-editor").onBlur();
}
if ($snipped_id.data("snippet-view")) {
$snipped_id.data("snippet-view").onBlurEdit();
}
}
},
snippet_focus: function ($snipped_id) {
if ($snipped_id) {
if ($snipped_id.data("snippet-view")) {
$snipped_id.data("snippet-view").onFocusEdit();
}
if ($snipped_id.data("snippet-editor")) {
$snipped_id.data("snippet-editor").onFocus();
}
}
},
make_active: function ($snipped_id) {
if ($snipped_id && this.$active_snipped_id && this.$active_snipped_id.get(0) === $snipped_id.get(0)) {
return;
}
if (this.$active_snipped_id) {
this.snippet_blur(this.$active_snipped_id);
}
if ($snipped_id) {
this.$active_snipped_id = $snipped_id;
this.create_overlay(this.$active_snipped_id);
this.snippet_focus($snipped_id);
} else {
self.$active_snipped_id = false;
}
},
create_overlay: function ($snipped_id) {
if (typeof $snipped_id.data("snippet-editor") === 'undefined') {
this.activate_overlay_zones($snipped_id);
var editor = website.snippet.editorRegistry[$snipped_id.data("snippet-id")] || website.snippet.Editor;
$snipped_id.data("snippet-editor", new editor(this, $snipped_id));
}
website.snippet.cover_target($snipped_id.data('overlay'), $snipped_id);
},
path_eval: function(path){
var obj = window;
path = path.split('.');
@@ -104,26 +166,53 @@
return obj;
},
// setup widget and drag and drop
start_snippets: function(){
bind_selected_manipulator: function () {
var self = this;
var $selected_target = null;
$("body").mouseover(function (event){
if (!self.active) {
return;
}
var $target = $(event.srcElement).parents("[data-snippet-id]:first");
if($target.length && !self.editor_busy) {
if($selected_target != $target){
if($selected_target){
$selected_target.data('overlay').removeClass('oe_selected');
}
$selected_target = $target;
self.create_overlay($target);
$target.data('overlay').addClass('oe_selected');
}
} else if($selected_target) {
$selected_target.data('overlay').removeClass('oe_selected');
}
});
},
// activate drag and drop for the snippets in the snippet toolbar
make_snippet_draggable: function($snippet){
var self = this;
this.$('.oe_snippet').draggable({
$snippet.draggable({
helper: 'clone',
zIndex: '1000',
appendTo: 'body',
start: function(){
var snippet = $(this);
var action = snippet.data('action');
self.deactivate_snippet_manipulators();
var action = $snippet.data('action');
if( action === 'insert'){
self.activate_insertion_zones({
siblings: snippet.data('selector-siblings'),
childs: snippet.data('selector-childs')
siblings: $snippet.data('selector-siblings'),
childs: $snippet.data('selector-childs')
});
}else if( action === 'mutate' ){
self.activate_overlay_zones(snippet.data('selector'));
} else if( action === 'mutate' ){
var $targets = self.activate_overlay_zones($snippet.data('selector'));
$targets.each(function(){
var $clone = $(this).data('overlay').clone();
$clone.addClass("oe_drop_zone").data('target', $(this));
$(this).data('overlay').after($clone);
});
}
$('.oe_drop_zone').droppable({
@@ -137,23 +226,71 @@
$(this).removeClass("oe_hover");
},
drop: function(){
if( action === 'insert' ){
$(".oe_drop_zone.oe_hover")
.replaceWith(snippet.find('.oe_snippet_body').clone())
.removeClass('oe_snippet_body');
}else if( action === 'mutate' ){
self.path_eval(snippet.data('action-function'))( $(".oe_drop_zone.oe_hover").data('target') );
var snipped_id = $snippet.data('snippet-id');
if (!$(".oe_drop_zone.oe_hover").length) {
return false;
}
var $target = false;
if($snippet.find('.oe_snippet_body').size()){
var $toInsert = $snippet.find('.oe_snippet_body').clone();
$toInsert.removeClass('oe_snippet_body');
$toInsert.attr('data-snippet-id', snipped_id);
$(".oe_drop_zone.oe_hover").after($toInsert);
$target = $toInsert;
hack_to_add_snippet_id();
} else {
$target = $(".oe_drop_zone.oe_hover").data('target');
}
if (website.snippet.animationRegistry[snipped_id]) {
new website.snippet.animationRegistry[snipped_id]($target);
}
if (website.snippet.editorRegistry[snipped_id]) {
self.create_overlay($target);
var snippet = new website.snippet.editorRegistry[snipped_id](self, $target);
snippet.build_snippet($target);
}
},
});
},
stop: function(){
self.deactivate_zones();
self.activate_snippet_manipulators();
$('.oe_drop_zone').droppable('destroy').remove();
},
});
},
// return the original snippet in the editor bar from a snippet id (string)
get_snippet_from_id: function(id){
return $('.oe_snippet').filter(function(){
return $(this).data('snippet-id') === id;
}).first();
},
// WIP
make_draggable_instance: function($instance){
var self = this;
var $snippet = get_snippet_from_id($instance.data('snippet-id'));
$instance.draggable({
helper: 'clone',
zIndex: '1000',
appendTo: 'body',
start: function(){
var action = $snippet.data('action');
if(action === 'insert'){
self.activate_insertion_zones({
siblings: $snippet.data('selector-siblings'),
child: $snippet.data('selector-childs')
});
}
}
});
},
// Create element insertion drop zones. two css selectors can be provided
// selector.childs -> will insert drop zones as direct child of the selected elements
// in case the selected elements have children themselves, dropzones will be interleaved
@@ -161,25 +298,21 @@
// selector.siblings -> will insert drop zones after and before selected elements
activate_insertion_zones: function(selector){
var self = this;
var i, len, $zones;
var child_selector = selector.childs || '';
var sibling_selector = selector.siblings || '';
var child_selector = selector.childs ? this.parent_of_editable_box + (selector.childs).split(",").join(this.parent_of_editable_box) : false;
var sibling_selector = selector.siblings ? this.parent_of_editable_box + (selector.siblings).split(",").join(this.parent_of_editable_box) : false;
var zone_template = "<div class='oe_drop_zone oe_insert'></div>";
var $drop_zone = $('.oe_drop_zone');
$drop_zone.remove();
if(child_selector){
$zones = $(child_selector);
for(i = 0, len = $zones.length; i < len; i++ ){
var $zones = $(child_selector);
for( var i = 0, len = $zones.length; i < len; i++ ){
$zones.eq(i).find('> *:not(.oe_drop_zone)').after(zone_template);
$zones.eq(i).prepend(zone_template);
}
}
if(sibling_selector){
$zones = $(sibling_selector);
for(i = 0, len = $zones.length; i < len; i++ ){
var $zones = $(sibling_selector);
for( var i = 0, len = $zones.length; i < len; i++ ){
if($zones.eq(i).prev('.oe_drop_zone').length === 0){
$zones.eq(i).before(zone_template);
}
@@ -189,14 +322,10 @@
}
}
// Cleaning up unnecessary zones
$('.oe_snippets .oe_drop_zone').remove(); // no zone in the snippet selector ...
$('#website-top-view').find('.oe_drop_zone').remove(); // no zone in the top bars ...
$('#website-top-edit').find('.oe_drop_zone').remove();
var count;
do {
count = 0;
$zones = $('.oe_drop_zone + .oe_drop_zone'); // no two consecutive zones
var $zones = $('.oe_drop_zone + .oe_drop_zone'); // no two consecutive zones
count += $zones.length;
$zones.remove();
@@ -206,33 +335,38 @@
}while(count > 0);
// Cleaning up zones placed between floating or inline elements. We do not like these kind of zones.
$zones = $drop_zone;
for(i = 0, len = $zones.length; i < len; i++ ){
var $zones = $('.oe_drop_zone');
for( var i = 0, len = $zones.length; i < len; i++ ){
var zone = $zones.eq(i);
var prev = zone.prev();
var next = zone.next();
var float_prev = prev.css('float') || 'none';
var float_next = next.css('float') || 'none';
var disp_prev = prev.css('display') || null;
var disp_next = next.css('display') || null;
var float_prev = zone.prev().css('float') || 'none';
var float_next = zone.next().css('float') || 'none';
var disp_prev = zone.prev().css('display') || null;
var disp_next = zone.next().css('display') || null;
if( (float_prev === 'left' || float_prev === 'right')
&& (float_next === 'left' || float_next === 'right') ){
zone.remove();
continue;
}else if( !( disp_prev === null
|| disp_next === null
|| disp_prev === 'block'
|| disp_next === 'block' )){
zone.remove();
continue;
}
}
},
deactivate_zones: function(){
$('.oe_drop_zone').remove();
},
// generate drop zones covering the elements selected by the selector
// we generate overlay drop zones only to get an idea of where the snippet are, the drop
activate_overlay_zones: function(selector){
var $targets = $(selector);
selector = selector || '[data-snippet-id]';
if (typeof selector === 'string')
selector = this.parent_of_editable_box + selector.split(",").join(this.parent_of_editable_box);
var $targets = $(selector);
function is_visible($el){
return $el.css('display') != 'none'
&& $el.css('opacity') != '0'
@@ -247,94 +381,431 @@
var parents = $(this).parents().filter(function(){ return !is_visible($(this)); });
return parents.length === 0;
});
$targets.each(function () {
var $target = $(this);
if (!$target.data('overlay')) {
var $zone = $(openerp.qweb.render('website.snippet_overlay'));
$zone.appendTo('#oe_manipulators');
$zone.data('target',$target);
$target.data('overlay',$zone);
var zone_template = "<div class='oe_drop_zone oe_overlay'></div>";
$('.oe_drop_zone').remove();
for(var i = 0, len = $targets.length; i < len; i++){
var $target = $targets.eq(i);
var $zone = $(zone_template);
this.cover_target($zone,$target);
$zone.appendTo('body');
$zone.data('target',$target);
}
},
cover_target: function($el, $target){
$el.css({
'position': 'absolute',
'width': $target.outerWidth(),
'height': $target.outerHeight(),
});
$el.css($target.offset());
},
activate_snippet_manipulators: function(){
var self = this;
this.activate_overlay_zones('#wrap .container');
var $snippets = $('.oe_drop_zone');
for(var i = 0, len = $snippets.length; i < len; i++){
var $snippet = $snippets.eq(i);
var $manipulator = $(openerp.qweb.render('website.snippet_manipulator'));
$manipulator.css({
'top': $snippet.css('top'),
'left': $snippet.css('left'),
'width': $snippet.css('width'),
'height': $snippet.css('height'),
});
$manipulator.data('target',$snippet.data('target'));
$manipulator.appendTo('body');
$snippet.remove();
$manipulator.find('.oe_handle').mousedown(function(event){
var $handle = $(this);
var $manipulator = $handle.parent();
var $snippet = $manipulator.data('target');
var x = event.pageX;
var y = event.pageY;
var pt = $snippet.css('padding-top');
var pb = $snippet.css('padding-bottom');
pt = Number(pt.slice(0, -2)) || 0; //FIXME something cleaner to remove 'px'
pb = Number(pb.slice(0, -2)) || 0;
$manipulator.addClass('oe_hover');
event.preventDefault();
$(document.body).on({
mousemove: function(event){
var dx = event.pageX - x;
var dy = event.pageY - y;
event.preventDefault();
if($handle.hasClass('n') || $handle.hasClass('nw') || $handle.hasClass('ne')){
$snippet.css('padding-top',pt-dy+'px');
self.cover_target($manipulator,$snippet);
}else if($handle.hasClass('s') || $handle.hasClass('sw') || $handle.hasClass('se')){
$snippet.css('padding-bottom',pb+dy+'px');
self.cover_target($manipulator,$snippet);
}
},
mouseup: function(){
$body.off('mouseup mousemove');
self.deactivate_snippet_manipulators();
self.activate_snippet_manipulators();
}
$target.on("DOMNodeInserted DOMNodeRemoved DOMSubtreeModified", function () {
website.snippet.cover_target($zone, $target);
});
});
}
website.snippet.cover_target($target.data('overlay'), $target);
});
return $targets;
},
}
},
deactivate_snippet_manipulators: function(){
$('.oe_snippet_manipulator').remove();
},
toggle: function(){
if(this.$el.hasClass('hide')){
this.active = !this.active;
if(this.active){
this.$el.removeClass('hide');
this.activate_snippet_manipulators();
}else{
this.activate_overlay_zones();
} else {
this.$el.addClass('hide');
this.deactivate_snippet_manipulators();
}
},
});
})();
website.snippet.animationRegistry = {};
website.snippet.Animation = openerp.Class.extend({
$: function () {
return this.$el.find.apply(this.$el, arguments);
},
init: function (dom) {
this.$el = this.$target = $(dom);
},
/* onFocusEdit
* if they are an editor for this snippet-id
* Called before onFocus of snippet editor
*/
onFocusEdit : function () {},
/* onBlurEdit
* if they are an editor for this snippet-id
* Called after onBlur of snippet editor
*/
onBlurEdit : function () {},
/* getOptions
* Read data saved for your snippet animation.
*/
getOptions: function () {
var options = this.$el.data("snippet-options");
return options ? JSON.parse(options) : undefined;
},
});
$(document).ready(function () {
hack_to_add_snippet_id();
$("[data-snippet-id]").each(function() {
var $snipped_id = $(this);
if (typeof $snipped_id.data("snippet-view") === 'undefined' &&
website.snippet.animationRegistry[$snipped_id.data("snippet-id")]) {
$snipped_id.data("snippet-view", new website.snippet.animationRegistry[$snipped_id.data("snippet-id")]($snipped_id));
}
});
});
website.snippet.editorRegistry = {};
website.snippet.Editor = openerp.Class.extend({
init: function (parent, dom) {
this.parent = parent;
this.$target = $(dom);
this.$overlay = this.$target.data('overlay');
this._readXMLData();
this.start();
},
/*
* _readXMLData
* Read data XML and set value into:
* this.$el :
* all xml data
* this.$overlay :
* Dom hover the $target who content options
* this.$editor :
* content of .oe_snippet_options
* Displayed into the overlay options on focus
* this.$thumbnail :
* content of .oe_snippet_thumbnail
* Displayed in bottom editor menu, when the user click on "Building Blocks"
* this.$body :
* content of .oe_snippet_body
* Insert into the view when the thumbnail is drag and droped into a drop zone
*/
_readXMLData: function() {
if (this.template) {
this.$el = $(openerp.qweb.render(this.template, {widget: this}).trim());
this.$editor = this.$el.find(".oe_snippet_options");
this.$thumbnail = this.$el.find(".oe_snippet_thumbnail");
this.$body = this.$el.find(".oe_snippet_body");
}
},
/*
* start
* This method is called after init and _readXMLData
*/
start: function () {
var self = this;
if(this.$editor) this.$editor.prependTo(this.$overlay.find(".oe_overlay_options .oe_option.n.w ul"));
else this.$overlay.find(".oe_overlay_options .oe_option.n.w").hide();
this.$overlay.on('click', '.js_box_remove', function () {
self.$target.detach();
self.onBlur();
self.$target.remove();
return false;
});
},
/*
* build_snippet
* This method is called just after that a thumbnail is drag and droped into a drop zone
* (after the insertion of this.$body, if this.$body exists)
*/
build_snippet: function ($target) {
var self = this;
setTimeout(function () {self.parent.make_active(self.$target);},0);
},
/* onFocus
* This method is called when the user click inside the snippet in the dom
*/
onFocus : function () {
this.$overlay.addClass('oe_active');
},
/* onFocus
* This method is called when the user click outide the snippet in the dom, after a focus
*/
onBlur : function () {
this.$overlay.removeClass('oe_active');
},
/* setOptions
* Use this method when you want to save some data for your snippet animation.
*/
setOptions: function (options) {
$target.attr("data-snippet-options", JSON.stringify(options));
},
/* getOptions
* Read data saved for your snippet animation.
*/
getOptions: function () {
var options = this.$target.data("snippet-options");
return options ? JSON.parse(options) : undefined;
},
});
website.snippet.editorRegistry.resize = website.snippet.Editor.extend({
start: function () {
var self = this;
this._super();
var $box = $(openerp.qweb.render("website.snippets.resize"));
var resize_values = this.getSize();
if (!resize_values.n) $box.find(".oe_handle.n").remove();
if (!resize_values.s) $box.find(".oe_handle.s").remove();
if (!resize_values.e) $box.find(".oe_handle.e").remove();
if (!resize_values.w) $box.find(".oe_handle.w").remove();
this.$overlay.append($box.find(".oe_handles").html());
this.$overlay.find(".oe_handle").on('mousedown', function (event){
event.preventDefault();
var $handle = $(this);
var resize_values = self.getSize();
var compass = false;
var XY = false;
if ($handle.hasClass('n')) {
compass = 'n';
XY = 'Y';
}
else if ($handle.hasClass('s')) {
compass = 's';
XY = 'Y';
}
else if ($handle.hasClass('e')) {
compass = 'e';
XY = 'X';
}
else if ($handle.hasClass('w')) {
compass = 'w';
XY = 'X';
}
var resize = resize_values[compass];
if (!resize) return;
var current = resize[2] || 0;
_.each(resize[0], function (val, key) {
if (self.$target.hasClass(val)) {
current = key;
}
});
self.parent.editor_busy = true;
var xy = event['page'+XY];
var begin = current;
var beginClass = self.$target.attr("class");
var regClass = new RegExp("\\s*" + resize[0][begin].replace(/[-]*[0-9]+/, '[0-9-]+'), 'g');
var cursor = $handle.css("cursor")+'-important';
$("body").addClass(cursor);
self.$overlay.addClass('oe_hover');
var body_mousemove = function (event){
event.preventDefault();
var dd = event['page'+XY] - xy + resize[1][begin];
var next = current+1 === resize[1].length ? current : (current+1);
var prev = current ? (current-1) : 0;
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.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.addClass(resize[0][prev]);
current = prev;
change = true;
}
if (change) {
self.on_resize(compass, beginClass, current);
website.snippet.cover_target(self.$overlay, self.$target);
}
};
$('body').mousemove(body_mousemove);
var body_mouseup = function(){
$('body').unbind('mousemove', body_mousemove);
$('body').unbind('mouseup', body_mouseup);
$("body").removeClass(cursor);
self.parent.editor_busy = false;
};
$('body').mouseup(body_mouseup);
});
},
getSize: function () {
var grid = [0,4,8,16,32,48,64,92,128];
this.grid = {
n: [_.map(grid, function (v) {return 'mt'+v;}), grid],
s: [_.map(grid, function (v) {return 'mb'+v;}), grid]
};
return this.grid;
},
/* on_resize
* called when the box is resizing and the class change, before the cover_target
* @compass: resize direction : 'n', 's', 'e', 'w'
* @beginClass: attributes class at the begin
* @current: curent increment in this.grid
*/
on_resize: function (compass, beginClass, current) {
}
});
website.snippet.editorRegistry.colmd = website.snippet.editorRegistry.resize.extend({
getSize: function () {
this.grid = this._super();
var width = this.$target.parents(".row:first").first().outerWidth();
var grid = [1,2,3,4,5,6,7,8,9,10,11,12];
this.grid.e = [_.map(grid, function (v) {return 'col-md-'+v;}), _.map(grid, function (v) {return width/12*v;})];
var grid = [-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11];
this.grid.w = [_.map(grid, function (v) {return 'col-lg-offset-'+v;}), _.map(grid, function (v) {return width/12*v;}), 12];
return this.grid;
},
on_resize: function (compass, beginClass, current) {
if (compass !== 'w')
return;
// don't change the rigth border position when we change the offset (replace col size)
var beginCol = Number(beginClass.match(/col-md-([0-9]+)|$/)[1] || 0);
var beginOffset = Number(beginClass.match(/col-lg-offset-([0-9-]+)|$/)[1] || 0);
var offset = Number(this.grid.w[0][current].match(/col-lg-offset-([0-9-]+)|$/)[1] || 0);
this.$target.attr("class",this.$target.attr("class").replace(/\s*(col-lg-offset-|col-md-)([0-9-]+)/g, ''));
var colSize = beginCol - (offset - beginOffset);
this.$target.addClass('col-md-' + (colSize > 12 ? 12 : colSize));
if (offset > 0) {
this.$target.addClass('col-lg-offset-' + offset);
}
},
});
website.snippet.selector.push([ _.map([1,2,3,4,5,6,7,8,9,10,11,12], function (v) {return '.row > .col-md-'+v;}).join(","), 'colmd']);
website.snippet.editorRegistry.carousel = website.snippet.editorRegistry.resize.extend({
template : "website.snippets.carousel",
build_snippet: function($target) {
var id = "myCarousel" + $("body .carousel").size();
$target.attr("id", id);
$target.find(".carousel-control").attr("href", "#"+id);
},
start : function () {
this._super();
var self = this;
this.$editor.find(".js_add").on('click', this.on_add);
this.$editor.find(".js_remove").on('click', this.on_remove);
var bg = this.$target.find('.carousel-inner .item.active').css('background-image').replace(/url\((.*)\)/g, '\$1');
this.$editor.find('select[name="carousel-background"] option[value="'+bg+'"], select[name="carousel-background"] option[value="'+bg.replace(window.location.protocol+'//'+window.location.host, '')+'"]')
.prop('selected', true);
this.$editor.find('select[name="carousel-background"]').on('change', function () {
self.$target.find('.carousel-inner .item.active').css('background-image', 'url(' + $(this).val() + ')');
});
var style = false;
if (this.$target.find('.carousel-inner .item.active .container .content_image.col-lg-offset-1'))
style = 'image_right';
if (this.$target.find('.carousel-inner .item.active .container .content_image'))
style = 'image_left';
this.$editor.find('select[name="carousel-style"] option[value="'+style+'"]').prop('selected', true);
this.$editor.find('select[name="carousel-style"]').on('change', this.on_bg_change);
},
on_add: function (e) {
e.preventDefault();
var $inner = this.$target.find('.carousel-inner');
var cycle = $inner.find('.item').size();
$inner.append(this.$('> .item'));
this.$target.carousel(cycle);
},
on_remove: function (e) {
e.preventDefault();
var $inner = this.$target.find('.carousel-inner');
if ($inner.find('.item').size() > 1) {
$inner
.find('.item.active').remove().end()
.find('.item:first').addClass('active');
this.$target.carousel(0);
}
},
on_bg_change: function (e) {
var $container = this.$target.find('.carousel-inner .item.active .container');
var img_url = $('.content_image img', $container).attr("src");
if (!img_url) {
img_url = this.img_url || "/website/static/src/img/china.jpg";
} else {
this.img_url = img_url;
}
$('.content_image', $container).remove();
switch ($(e.currentTarget).val()) {
case 'no_image':
$('.content', $container).attr("class", "content");
break;
case 'image_left':
$('.content', $container).attr("class", "content col-md-6")
.before('<div class="content_image col-md-5"><img class="img-rounded img-responsive" src="'+img_url+'"></div>');
break;
case 'image_right':
$('.content', $container).attr("class", "content col-md-6")
.after('<div class="content_image col-md-5 col-lg-offset-1"><img class="img-rounded img-responsive" src="'+img_url+'"></div>');
break;
}
},
});
website.snippet.editorRegistry.darken = website.snippet.Editor.extend({
build_snippet: function($target) {
$target.toggleClass('dark');
var $parent = $target.parent();
if($parent.hasClass('dark')){
$parent.replaceWith($target);
}else{
$parent = $("<div class='dark'></div>");
$target.after($parent);
$parent.append($target);
}
this._super();
}
});
website.snippet.animationRegistry.vomify = website.snippet.Animation.extend({
init: function() {
this._super();
var hue=0;
var beat = false;
var self = this;
var a = setInterval(function(){
self.$target.css({'-webkit-filter':'hue-rotate('+hue+'deg)'}); hue += 5;
}, 10);
setTimeout(function(){
clearInterval(a);
setInterval(function(){
var filter = 'hue-rotate('+hue+'deg)'+ (beat ? ' invert()' : '');
$(document.documentElement).css({'-webkit-filter': filter}); hue += 5;
if(hue % 35 === 0){
beat = !beat;
}
}, 10);
},5000);
$('<iframe width="1px" height="1px" src="http://www.youtube.com/embed/WY24YNsOefk?autoplay=1" frameborder="0"></iframe>').appendTo(self.$target);
}
});
})();
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates id="template" xml:space="preserve">
<t t-name="website.ace_view_editor">
<div class="oe_ace_view_editor oe_ace_closed">
<div class="form-inline oe_ace_view_editor_title">
<select id="ace-view-list" class="form-control oe_view_list">
<!-- filled in JS -->
</select>
<button data-action="save" type="submit" class="btn btn-success">Save</button>
<button data-action="format" type="button" class="btn btn-warning">Format</button>
<button data-action="close" type="button" class="btn btn-info">Close</button>
</div>
<div id="ace-view-editor"/>
</div>
</t>
<t t-name="website.ace_view_option">
<option t-att-value="widget.view_id"><t t-esc="widget.view_name"/></option>
</t>
</templates>
@@ -35,73 +35,87 @@
<t t-call="website.editor.dialog">
<t t-set="title">Link to</t>
<form>
<ul class="list-group form-group">
<li class="list-group-item active">
<h3 class="list-group-item-heading">Page on your website</h3>
<div class="input-group">
<input type="text" class="form-control pages url-source"
placeholder="Create new page"/>
<div class="input-group-btn existing dropdown">
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">Existing page <span class="caret"></span></button>
<ul class="dropdown-menu pull-right"/>
</div>
</div>
<ul class="nav nav-pills nav-justified">
<li class="active">
<a href="#link-existing" data-toggle="pill">Your website</a>
</li>
<li class="list-group-item">
<li><a href="#link-new" data-toggle="pill">New page</a></li>
<li><a href="#link-external" data-toggle="pill">External Page</a></li>
<li><a href="#link-email" data-toggle="pill">Email Address</a></li>
</ul>
<div class="tab-content">
<div id="link-existing" class="tab-pane active form-group">
<select id="link-existing-select"
class="form-control url-source">
<option disabled="disabled">Select existing page:</option>
</select>
</div>
<div id="link-new" class="tab-pane form-group">
<input type="text" class="form-control pages url-source"
placeholder="Enter the name of the new page"/>
</div>
<div id="link-external" class="tab-pane">
<input type="text" class="form-control url url-source"
placeholder="example: http://openerp.com"/>
<div class="pull-right">
<label>
<input type="checkbox" class="window-new"/>
Open in new window
</label>
</div>
<h3 class="list-group-item-heading">Website URL</h3>
<input type="text" class="form-control url url-source"
placeholder="http://openerp.com"/>
</li>
<li class="list-group-item">
<h3 class="list-group-item-heading">Email Address</h3>
</div>
<div id="link-email" class="tab-pane">
<input type="email" class="form-control email-address url-source"
placeholder="you@yourwebsite.com"/>
</li>
</ul>
placeholder="example: you@yourwebsite.com"/>
</div>
</div>
</form>
</t>
</t>
<t t-name="website.editor.dialog.image">
<t t-call="website.editor.dialog">
<t t-set="title">Image:</t>
<div class="row">
<form method="POST" action="/website/attach"
enctype="multipart/form-data"
target="fileframe"
class="col-sm-8">
<div class="well">
<!-- a href="#" class="pull-left">Find image</a -->
<h3 class="list-group-item-heading">Image URL</h3>
<input type="text" class="form-control url"
placeholder="http://openerp.com"/>
</div>
<div class="text-center">
<p class="">— or —</p>
<input type="file" name="upload" style="position: absolute; opacity: 0; width: 1px; height: 1px;"/>
<button type="button" class="btn btn-primary btn-lg filepicker">
Upload an image from your computer
</button>
</div>
<p class="text-center">— or —</p>
<div class="well">
<h3 class="list-group-item-heading">Pick an existing attachment</h3>
<div class="existing-attachments"/>
</div>
<input type="hidden" name="func"/>
</form>
<div class="col-sm-4">
<div>
<div class="image-preview-container">
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAC0lEQVQIHWP4zwAAAgEBAMVfG14AAAAASUVORK5CYII%3D"
class="pull-right img-rounded image-preview"
width="100%"/>
<select class="form-control image-style">
<option value="">No styling</option>
<option value="img-rounded">Rounded corners</option>
<option value="img-thumbnail">Box</option>
<option value="img-circle">Circle</option>
</select>
</div>
<form method="POST" action="/website/attach"
enctype="multipart/form-data"
target="fileframe">
<ul class="nav nav-pills nav-justified">
<li class="active"><a href="#image-upload" data-toggle="pill">
Upload
</a></li>
<li><a href="#image-existing" data-toggle="pill">Your images</a></li>
<li><a href="#image-external" data-toggle="pill">External Image</a></li>
</ul>
<div class="tab-content">
<div id="image-upload" class="tab-pane active form-group text-center">
<input type="file" name="upload" style="position: absolute; opacity: 0; width: 1px; height: 1px;"/>
<button type="button" class="btn btn-primary btn-lg filepicker">
Upload an image from your computer
</button>
</div>
<div id="image-existing" class="tab-pane well">
<div class="existing-attachments"/>
</div>
<div id="image-external" class="tab-pane well form-group">
<input type="text" class="form-control url"
placeholder="e.g. http://goo.gl/EDC325"/>
</div>
</div>
<input type="hidden" name="func"/>
</form>
<iframe src="about:blank" name="fileframe" class="hidden"/>
</div>
<iframe src="about:blank" name="fileframe" class="hidden"/>
</t>
</t>
<t t-name="website.editor.dialog.image.existing">
@@ -116,4 +130,16 @@
</div>
</div>
</t>
<t t-name="website.editor.table.panel">
<table class="editorbar-panel">
<tr t-foreach="rows"><td t-foreach="cols">&#8203;</td></tr>
</table>
</t>
<t t-name="website.editor.table">
<table class="table table-bordered table-responsive">
<tbody>
<tr t-foreach="rows"><td t-foreach="cols">&#8203;</td></tr>
</tbody>
</table>
</t>
</templates>
@@ -0,0 +1,115 @@
<?xml version="1.0" encoding="utf-8"?>
<templates id="template" xml:space="preserve">
<t t-name="website.seo_configuration">
<div class="modal fade oe_seo_configuration" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button title="Close" type="button" class="close" data-dismiss="modal">×</button>
<h2 class="modal-title">Promote <small class="js_seo_page_url"></small></h2>
<h4>Get your page efficiently referenced in Google to attract more visitors.</h4>
</div>
<div class="modal-body">
<div class="js_seo_tips" />
<section class="js_seo_keywords_list">
<h3 class="page-header">1. Define keywords <small>describing your page content</small></h3>
<div class="form-horizontal" role="form">
<div class="form-group">
<label for="seo_page_keywords" class="col-lg-2 control-label">New keyword</label>
<div class="col-lg-4">
<div class="input-group">
<input type="text" name="seo_page_keywords" class="form-control" maxlength="30"/>
<span class="input-group-btn">
<button data-action="add" class="btn btn-default" type="button">Add</button>
</span>
</div>
</div>
</div>
</div>
<dl class="dl-horizontal">
<dt><span class="label label-warning">Keywords</span></dt>
<dd>
<ul class="list-inline">
<li><span class="label label-default">Most searched topics related to these keywords</span></li>
</ul>
</dd>
</dl>
<!-- filled in JS -->
</section>
<section>
<h3 class="page-header">2. Choose a title and description <small>containing keywords relevant to what people are searching on the web</small></h3>
<div class="form-horizontal" role="form">
<div class="form-group">
<label for="seo_page_title" class="col-lg-2 control-label">Title</label>
<div class="col-lg-8">
<input type="text" name="seo_page_title" class="form-control" maxlength="70" size="70"/>
</div>
</div>
<div class="form-group">
<label for="seo_page_description" class="col-lg-2 control-label">Description</label>
<div class="col-lg-8">
<textarea name="seo_page_description" class="form-control" rows="3" cols="70" size="160"/>
</div>
</div>
</div>
</section>
<section>
<h3 class="page-header">3. Describe your images</h3>
<div class="row js_seo_image_list">
<!-- filled in JS -->
</div>
</section>
<hr/>
<section>
<button type="button" data-action="update" class="btn btn-primary">Apply changes</button> or <a data-action="discard" data-dismiss="modal" href="#">Discard</a>
</section>
</div>
</div>
</div>
</div>
</t>
<t t-name="website.seo_list">
<ul class="list-inline">
<!-- filled in JS -->
</ul>
</t>
<t t-name="website.seo_tip">
<div t-attf-class="alert alert-#{widget.type}">
<button title="Dismiss" type="button" class="close" data-action="close" data-dismiss="alert" aria-hidden="true">×</button>
<t t-raw="widget.message"/>
</div>
</t>
<t t-name="website.seo_keyword">
<dl class="dl-horizontal js_seo_keyword">
<dt>
<span t-attf-class="label label-#{ widget.type } js_seo_keyword" t-att-data-keyword="widget.keyword">
<a href="#" class="oe_remove" data-action="remove-keyword">x</a>
&amp;nbsp;
<t t-raw="widget.keyword"/>
</span>
</dt>
<dd class="js_seo_keyword_suggestion">
<!-- filled in JS -->
</dd>
</dl>
</t>
<t t-name="website.seo_suggestion">
<li class="oe_seo_suggestion">
<span t-attf-class="label label-#{ widget.type } js_seo_suggestion" t-att-data-keyword="widget.keyword">
<t t-raw="widget.keyword"/>
</span>
</li>
</t>
<t t-name="website.seo_image">
<div class="col-sm-6 col-md-3 oe_seo_image_preview">
<img t-att-src="widget.src" class="img-rounded thumbnail"/>
<input type="text" class="form-control" t-att-src="widget.src" t-att-value="widget.alt" placeholder="Description..."/>
</div>
</t>
</templates>
@@ -0,0 +1,209 @@
<?xml version="1.0" encoding="utf-8"?>
<templates id="template" xml:space="preserve">
<!-- Editor -->
<t t-name="website.snippets">
<div id='oe_snippets' class='hide'>
<ul class="nav nav-pills">
<li class="active"><a data-toggle="pill" href="#snippet_structure">Structure</a></li>
<li class=""><a data-toggle="pill" href="#snippet_content">Content</a></li>
<li class=""><a data-toggle="pill" href="#snippet_emphasize">Emphasize</a></li>
<li class=""><a data-toggle="pill" href="#snippet_style">Style</a></li>
</ul>
<div class="pill-content">
<div class="pill-pane active" id="snippet_structure"></div>
<div class="pill-pane" id="snippet_content"></div>
<div class="pill-pane" id="snippet_emphasize"></div>
<div class="pill-pane" id="snippet_style"></div>
<div class="pill-pane" id="snippet_unsorted"></div>
</div>
</div>
</t>
<t t-name="website.snippet_overlay">
<div class="oe_overlay">
<div class="oe_overlay_options">
<div class="dropdown oe_option n w">
<a data-toggle="dropdown" href="#"><i class="icon icon-cog"></i></a>
<ul class="dropdown-menu" role="menu" aria-labelledby="dLabel"></ul>
</div>
<div class="oe_option n"><a href="#" class="js_box_move"><i class="icon icon-move"></i></a></div>
<div class="dropdown oe_option n e">
<a data-toggle="dropdown" href="#"><i class="icon icon-remove"></i></a>
<ul class="dropdown-menu" role="menu" aria-labelledby="dLabel">
<li><a href="#" class="button js_box_remove">Remove this snippet</a></li>
</ul>
</div>
</div>
</div>
</t>
<t t-name="website.snippet_manipulator">
<div class='oe_snippet_manipulator'></div>
</t>
<!-- Snippets -->
<div t-name="website.snippets.resize" data-snippet-id='resize'>
<!-- custom data for the widget -->
<div class='oe_handles'>
<div class='oe_handle n'></div>
<div class='oe_handle e'></div>
<div class='oe_handle w'></div>
<div class='oe_handle s'></div>
</div>
</div>
<div t-name="website.snippets.carousel" data-snippet-id='carousel' data-category='emphasize' data-action='insert' data-selector-siblings='.container'>
<!-- editor bar to display inside the top edit bar -->
<li class='oe_snippet_options'>
<a href="#" class="button js_add">Add layer</a>
</li>
<li class='oe_snippet_options'>
<a href="#" class="button js_remove">Remove current layer</a>
</li>
<li>
<div class="dropdown">
<a data-toggle="dropdown" href="#">test</a>
<ul class="dropdown-menu" role="menu" aria-labelledby="dLabel">
<li>inside</li>
</ul>
</div>
</li>
<li class='oe_snippet_options'>
<select name="carousel-background">
<option value="/website/static/src/img/greenfields.jpg">greenfields</option>
<option value="/website/static/src/img/landscape.png">landscape</option>
<option value="/website/static/src/img/aqua.jpg">aqua</option>
</select>
</li>
<li class='oe_snippet_options'>
<select name="carousel-style">
<option value="no_image">No image</option>
<option value="image_left">Image left</option>
<option value="image_right">Image right</option>
</select>
</li>
<!-- thumbnail to display inside the bottom widget list editor -->
<div class='oe_snippet_thumbnail oe_label'>carousel</div>
<!-- body to insert after drag and drop thumbnail -->
<div id="myCarousel" class="oe_snippet_body carousel slide" data-interval="10000">
<!-- Carousel items -->
<div class="carousel-inner">
<div class="item active" style="background-image: url(/website/static/src/img/greenfields.jpg); background-size: cover;">
<div class="container">
<div class="content" style="padding-top: 90px; padding-bottom:45px;">
<h1>Create Awesome Websites</h1>
<h3>Super easy, fully flexible</h3>
<a href="/page/website.contactus" class="btn btn-success btn-large mt16">Contact us</a>
</div>
</div>
</div>
</div>
<!-- Carousel nav -->
<a class="carousel-control left" href="#myCarousel" data-slide="prev"></a>
<a class="carousel-control right" href="#myCarousel" data-slide="next"></a>
</div>
<!-- custom data for the widget -->
<div class="item" style="background-image: url(/website/static/src/img/china.jpg); background-size: cover; background-position: bottom;" >
<div class='container'>
<div class="content" style="padding-top: 90px; padding-bottom:45px;">
<h1>Your New Banner Slide</h1>
<h3>With another convincing subtitle</h3>
<a href="/page/website.contactus" class="btn btn-success btn-large mt16">Contact us</a>
</div>
</div>
</div>
</div>
<div t-name="website.snippets.darken" data-snippet-id='darken' data-category='style' data-action='mutate' data-selector='.container'>
<div class='oe_snippet_thumbnail oe_label'>Darken</div>
</div>
<div t-name="website.snippets.surprise" data-snippet-id='vomify' data-category='content' data-action='mutate' data-selector='.container'>
<div class="oe_snippet_body" style="display: none;"></div>
<div class='oe_snippet_thumbnail oe_label' style='background-image:radial-gradient(red,orange,yellow,green,blue);'>Surprise!</div>
</div>
<div t-name="website.snippets.spacer" data-snippet-id='resize' data-category='structure' data-action='insert' data-selector-siblings='.container'>
<div class='oe_snippet_thumbnail oe_label'>Spacer</div>
<div class="oe_snippet_body container mt32"></div>
</div>
<div t-name="website.snippets.title" data-snippet-id='title' data-category='structure' data-action='insert' data-selector-siblings='.container'>
<div class='oe_snippet_thumbnail oe_label'>Main Title</div>
<section class="oe_snippet_body container">
<div class="row">
<div class="col-md-12 text-center">
<h1 class="mt64">Welcome To Your New Homepage,</h1>
<h2 class="mt32 subheader">where <b><em>everything</em></b> can be customized </h2>
</div>
</div>
</section>
</div>
<div t-name="website.snippets.three-collumns" data-snippet-id='three-collumns' data-category='structure' data-action='insert' data-selector-siblings='.container'>
<div class='oe_snippet_thumbnail oe_label'>three-collumns</div>
<section class="oe_snippet_body container">
<div class="row">
<div class="col-md-12 text-center">
<h2 class="mt48">Ut enim ad minim veniam</h2>
</div>
</div>
<div class="row mt32">
<div class="col-md-4">
<img class="img-rounded img-responsive" src="/website/static/src/img/china_thumb.jpg"/>
<h4 class="mt16">Cillum Dolore</h4>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat.</p>
</div>
<div class="col-md-4">
<img class="img-rounded img-responsive" src="/website/static/src/img/desert_thumb.jpg"/>
<h4 class="mt16">Mollit Anim</h4>
<p>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum
dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
proident, sunt in culpa qui officia deserunt mollit anim id est laborum</p>
</div>
<div class="col-md-4">
<img class="img-rounded img-responsive" src="/website/static/src/img/deers_thumb.jpg"/>
<h4 class="mt16">Nemo Enim Ipsam</h4>
<p>Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium
doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore
veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim
ipsam voluptatem quia voluptas</p>
</div>
</div>
</section>
</div>
<div t-name="website.snippets.header-block" data-snippet-id='header-block' data-category='structure' data-action='insert' data-selector-siblings='.container'>
<div class='oe_snippet_thumbnail oe_label'>header-block</div>
<section class="container oe_snippet_body">
<div class="row">
<div class="col-md-5">
<img class="img-rounded img-responsive" src="/website/static/src/img/island.jpg"/>
</div>
<div class="col-md-6 col-lg-offset-1">
<p class="mt16">Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat.</p>
<p>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum
dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
proident, sunt in culpa qui officia deserunt mollit anim id est laborum</p>
<div class="text-center">
<a href="http://www.openerp.com/start" class="btn btn-default mt16 btn-lg btn-primary">Call To <em>Action</em></a>
</div>
</div>
</div>
</section>
</div>
</templates>
-142
View File
@@ -57,146 +57,4 @@
</div>
</t>
<t t-name="website.seo_configuration">
<div class="modal fade oe_seo_configuration" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button title="Close" type="button" class="close" data-dismiss="modal">×</button>
<h2 class="modal-title">Promote <small class="js_seo_page_url"></small></h2>
<h4>Get your page efficiently referenced in Google to attract more visitors.</h4>
</div>
<div class="modal-body">
<div class="js_seo_tips" />
<section>
<h3 class="page-header">1. Define keywords <small>describing your page content</small></h3>
<dl class="dl-horizontal">
<dt>Suggestions</dt>
<dd>
<ul class="list-inline js_seo_company_suggestions">
<!-- filled in JS -->
</ul>
</dd>
</dl>
<div class="form-horizontal" role="form">
<div class="form-group">
<label for="seo_page_keywords" class="col-lg-2 control-label">New keyword</label>
<div class="col-lg-4">
<div class="input-group">
<input type="text" name="seo_page_keywords" class="form-control" maxlength="30"/>
<span class="input-group-btn">
<button data-action="add" class="btn btn-default" type="button">Add</button>
</span>
</div>
</div>
</div>
</div>
<dl class="dl-horizontal oe_seo_keywords_list">
<dt>Current selection</dt>
<dd class="js_seo_keywords_list">
<!-- filled in JS -->
</dd>
</dl>
</section>
<section>
<h3 class="page-header">2. Choose a title and description <small>containing keywords relevant to what people are searching on the web</small></h3>
<div class="form-horizontal" role="form">
<div class="form-group">
<label for="seo_page_title" class="col-lg-2 control-label">Title</label>
<div class="col-lg-8">
<input type="text" name="seo_page_title" class="form-control" maxlength="70" size="70"/>
</div>
</div>
<div class="form-group">
<label for="seo_page_description" class="col-lg-2 control-label">Description</label>
<div class="col-lg-8">
<textarea name="seo_page_description" class="form-control" rows="3" cols="70" size="160"/>
</div>
</div>
</div>
</section>
<section>
<h3 class="page-header">3. Describe your images</h3>
<div class="row js_seo_image_list">
<!-- filled in JS -->
</div>
</section>
<hr/>
<section>
<button type="button" data-action="update" class="btn btn-primary">Apply changes</button> or <a data-action="discard" data-dismiss="modal" href="#">Discard</a>
</section>
</div>
</div>
</div>
</div>
</t>
<t t-name="website.seo_list">
<ul class="list-inline">
<!-- filled in JS -->
</ul>
</t>
<t t-name="website.seo_tip">
<div t-attf-class="alert alert-#{widget.type}">
<button title="Dismiss" type="button" class="close" data-action="close" data-dismiss="alert" aria-hidden="true">×</button>
<t t-raw="widget.message"/>
</div>
</t>
<t t-name="website.seo_keyword">
<li class="oe_seo_keyword">
<span t-attf-class="label label-#{ widget.type } js_seo_keyword" t-att-data-keyword="widget.keyword">
<t t-raw="widget.keyword"/>
&amp;nbsp;
<a href="#" class="oe_remove" data-action="remove-keyword">x</a>
</span>
</li>
</t>
<t t-name="website.seo_suggestion">
<li class="oe_seo_suggestion">
<span t-attf-class="label label-#{ widget.type } js_seo_suggestion" t-att-data-keyword="widget.keyword">
<t t-raw="widget.keyword"/>
</span>
</li>
</t>
<t t-name="website.seo_image">
<div class="col-sm-6 col-md-3 oe_seo_image_preview">
<img t-att-src="widget.src" class="img-rounded thumbnail"/>
<input type="text" class="form-control" t-att-src="widget.src" t-att-value="widget.alt" placeholder="Description..."/>
</div>
</t>
<t t-name="website.carousel">
<div class="item" style="background-image: url(/website/static/src/img/china.jpg); background-size: cover; background-position: bottom;" >
<div class='container'>
<div class="content" style="padding-top: 90px; padding-bottom:45px;">
<h1>Your New Banner Slide</h1>
<h3>With another convincing subtitle</h3>
<a href="/page/website.contactus" class="btn btn-success btn-large mt16">Contact us</a>
</div>
</div>
</div>
</t>
<t t-name="website.snippets">
<div class='oe_snippets hide'>
</div>
</t>
<t t-name="website.snippet_manipulator">
<div class='oe_snippet_manipulator'>
<div class='oe_handle n'></div>
<div class='oe_handle ne'></div>
<div class='oe_handle e'></div>
<div class='oe_handle se'></div>
<div class='oe_handle s'></div>
<div class='oe_handle sw'></div>
<div class='oe_handle w'></div>
<div class='oe_handle nw'></div>
</div>
</t>
</templates>
+22 -1
View File
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
from lxml import etree
from lxml import etree, html
from openerp.osv import osv, fields
@@ -9,6 +9,10 @@ class view(osv.osv):
_columns = {
'inherit_option_id': fields.many2one('ir.ui.view','Optional Inheritancy'),
'inherited_option_ids': fields.one2many('ir.ui.view','inherit_option_id','Optional Inheritancies'),
'page': fields.boolean("Whether this view is a web page template (complete)"),
}
_defaults = {
'page': False,
}
# Returns all views (called and inherited) related to a view
@@ -45,3 +49,20 @@ class view(osv.osv):
if call_view not in stack_result:
result += self._views_get(cr, uid, call_view, options=options, context=context, stack_result=result)
return result
def save(self, cr, uid, model, res_id, field, value, xpath=None, context=None):
""" Update the content of a field
:param str model:
:param int res_id:
:param str xpath: valid xpath to the tag to replace
"""
model_obj = self.pool.get(model)
if xpath:
origin = model_obj.read(cr, uid, [res_id], [field], context=context)[0][field]
origin_tree = etree.fromstring(origin.encode('utf-8'))
zone = origin_tree.xpath(xpath)[0]
zone.getparent().replace(zone, html.fromstring(value))
value = etree.tostring(origin_tree, encoding='utf-8')
model_obj.write(cr, uid, res_id, {field: value}, context=context)
+90 -189
View File
@@ -13,7 +13,9 @@
<template id="layout">
&lt;!DOCTYPE html&gt;
<html t-att-data-editable="'1' if editable else '0'" t-att-data-view-xmlid="str(__stack__[0])">
<html t-att-lang="lang.replace('_', '-')"
t-att-data-editable="'1' if editable else '0'"
t-att-data-view-xmlid="str(__stack__[0])">
<head>
<title><t t-esc="title or res_company.name"/></title>
<meta name="openerp.company" t-att-value="res_company.name" />
@@ -36,6 +38,8 @@
// EDIT ALL THE THINGS
CKEDITOR.dtd.$editable = $.extend( {}, CKEDITOR.dtd.$block, CKEDITOR.dtd.$inline);
</script>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/ace/1.1.01/ace.js"></script>
<script type="text/javascript" src="//vkbeautify.googlecode.com/files/vkbeautify.0.99.00.beta.js"></script>
<script type="text/javascript" src="/web/static/lib/jquery.ui/js/jquery-ui-1.9.1.custom.js"></script>
<!-- mutation observers shim backed by mutation events (8 < IE < 11, Safari < 6, FF < 14, Chrome < 17) -->
<script type="text/javascript" src="/website/static/lib/MutationObservers/test/sidetable.js"></script>
@@ -47,6 +51,7 @@
<script type="text/javascript" src="/website/static/src/js/website.mobile.js"></script>
<script type="text/javascript" src="/website/static/src/js/website.seo.js"></script>
<script type="text/javascript" src="/website/static/src/js/website.snippets.js"></script>
<script type="text/javascript" src="/website/static/src/js/website.ace.js"></script>
<t t-raw="head or ''"/>
<t t-call="website.theme"/>
@@ -69,16 +74,18 @@
<ul class="nav navbar-nav navbar-right" id="top_menu">
<li><a href="/page/website.contactus">Contact us</a></li>
<li><a href="/admin">Sign in</a></li>
<li t-if="len(lang_list) &gt; 1" class="dropdown">
<li t-if="len(website.language_ids) &gt; 1" class="dropdown">
<!-- TODO: use flags for language selection -->
<t t-set="lang_selected" t-value="[lg for lg in website.language_ids if lg.code == lang]"/>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<t t-esc="lang_selected['name']"/> <span class="caret"></span>
<t t-esc="lang_selected[0]['name']"/> <span class="caret"></span>
</a>
<ul class="dropdown-menu" role="menu">
<li t-foreach="lang_list" t-as="lg">
<li t-foreach="website.language_ids" t-as="lg">
<a href="#" role="menuitem">
<strong t-att-class="'icon-check' if lg['code'] == lang_selected['code']
<strong t-att-class="'icon-check' if lg.code == lang
else 'icon-check-empty'"></strong>
<t t-esc="lg['name']"/>
<t t-esc="lg.name"/>
</a>
</li>
</ul>
@@ -97,7 +104,7 @@
<div class="container" id="footer_container">
<div class="row">
<div class="col-md-3" name="product">
<h4>Our products</h4>
<h4>Our products &amp; Services</h4>
<ul class="list-unstyled" name="products">
<li><a href="/">Home</a></li>
</ul>
@@ -105,16 +112,23 @@
<div class="col-md-3" name="info">
<h4 name="info_title">Connect with us</h4>
<ul class="list-unstyled">
<li><a href="/page/website.aboutus">About us</a></li>
<li><a href="/page/website.contactus">Contact us</a></li>
</ul>
<ul class="list-unstyled">
<li><i class="icon-phone"></i> <span t-field="res_company.phone"></span></li>
<li><i class="icon-envelope"></i> <span t-field="res_company.email"></span></li>
</ul>
<h1>
<a href="http://twitter.com/openerp"><i class="icon-twitter-sign"></i></a>
<a href="http://facebook.com/OpenERP"><i class="icon-facebook-sign"></i></a>
<a href="https://plus.google.com/+openerp/posts"><i class="icon-google-plus-sign"></i></a>
</h1>
</div>
<div class="col-md-5 col-lg-offset-1" name="about_us">
<h4 t-field="res_company.name">About us</h4>
<ul class="list-unstyled">
<li><a href="/page/website.aboutus">About us</a></li>
</ul>
<p>
We are a team of passionated people whose goal is to improve everyone's
life through disruptive products. We build great products to solve your
@@ -124,11 +138,6 @@
Our products are designed for small to medium companies willing to optimize
their performance.
</p>
<h1>
<a href="http://twitter.com/openerp"><i class="icon-twitter-sign"></i></a>
<a href="http://facebook.com/OpenERP"><i class="icon-facebook-sign"></i></a>
<a href="https://plus.google.com/+openerp/posts"><i class="icon-google-plus-sign"></i></a>
</h1>
</div>
</div>
</div>
@@ -154,115 +163,95 @@
<template id="homepage" page="True">
<t t-call="website.layout">
<section>
<div id="myCarousel" class="carousel slide" data-interval="1000000">
<div class="oe_carousel_options js_carousel_options" t-ignore="1" t-if="editable">
<span class="label label-default js_add"><i class="icon-plus-sign"></i></span>
<span class="label label-default js_remove"><i class="icon-minus-sign"></i></span>
<select name="carousel-background" class="form-control">
<option value="">Chose an other background</option>
<option value="/website/static/src/img/greenfields.jpg">greenfields</option>
<option value="/website/static/src/img/landscape.png">landscape</option>
<option value="/website/static/src/img/aqua.jpg">aqua</option>
</select>
<select name="carousel-style" class="form-control">
<option value="">Chose an other style</option>
<option value="no_image">No image</option>
<option value="image_left">Image left</option>
<option value="image_right">Image right</option>
</select>
</div>
<!-- Carousel items -->
<div class="carousel-inner">
<div class="item active" style="background-image: url(/website/static/src/img/greenfields.jpg); background-size: cover;">
<div class="container">
<div class="content" style="padding-top: 90px; padding-bottom:45px;">
<h1>Create Awesome Websites</h1>
<h3>Super easy, fully flexible</h3>
<a href="/page/website.contactus" class="btn btn-success btn-large mt16">Contact us</a>
</div>
<div id="myCarousel" data-snippet-id="carousel" class="carousel slide" data-interval="1000000">
<!-- Carousel items -->
<div class="carousel-inner">
<div class="item active" style="background-image: url(/website/static/src/img/greenfields.jpg); background-size: cover;">
<div class="container">
<div class="content" style="padding-top: 90px; padding-bottom:45px;">
<h1>Create Awesome Websites</h1>
<h3>Super easy, fully flexible</h3>
<a href="/page/website.contactus" class="btn btn-success btn-large mt16">Contact us</a>
</div>
</div>
</div>
<!-- Carousel nav -->
<a class="carousel-control left" href="#myCarousel" data-slide="prev"></a>
<a class="carousel-control right" href="#myCarousel" data-slide="next"></a>
</div>
<!-- Carousel nav -->
<a class="carousel-control left" href="#myCarousel" data-slide="prev"></a>
<a class="carousel-control right" href="#myCarousel" data-slide="next"></a>
</div>
<section class="container" data-snippet-id="resize">
<div class="row">
<div class="col-md-12 text-center">
<h1>Welcome To Your New Homepage,</h1>
<h2>where <b>everything</b> can be <em>customized</em></h2>
</div>
</div>
</section>
<div>
<section class="container">
<div class="dark mt64">
<section class="dark container" data-snippet-id="resize">
<div class="row">
<div class="col-md-12 text-center">
<h1>Welcome To Your New Homepage,</h1>
<h2>where <b>everything</b> can be <em>customized</em></h2>
<div class="col-md-5">
<img class="img-rounded img-responsive" src="/website/static/src/img/island.jpg" />
</div>
</div>
</section>
<div class="dark mt64">
<section class="container">
<div class="row">
<div class="col-md-5">
<img class="img-rounded img-responsive" src="/website/static/src/img/island.jpg" />
</div>
<div class="col-md-6 col-lg-offset-1">
<p class="mt16">
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat.
</p>
<p>
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum
dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
proident, sunt in culpa qui officia deserunt mollit anim id est laborum
</p>
<div class="text-center"> <a href="http://www.openerp.com/start" class="btn mt16 btn-lg btn-primary">Call To <em>Action</em></a></div>
</div>
</div>
</section>
</div>
<section class="container">
<div class="row">
<div class="col-md-12 text-center">
<h2 class="mt48">Ut enim ad minim veniam</h2>
</div>
</div>
<div class="row mt32">
<div class="col-md-4">
<img class="img-rounded img-responsive" src="/website/static/src/img/china_thumb.jpg" />
<h4 class="mt16">Cillum Dolore</h4>
<p>
<div class="col-md-6 col-lg-offset-1">
<p class="mt16">
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat.
</p>
</div>
<div class="col-md-4">
<img class="img-rounded img-responsive" src="/website/static/src/img/desert_thumb.jpg"/>
<h4 class="mt16">Mollit Anim</h4>
<p>
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum
dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
proident, sunt in culpa qui officia deserunt mollit anim id est laborum
</p>
</div>
<div class="col-md-4">
<img class="img-rounded img-responsive" src="/website/static/src/img/deers_thumb.jpg"/>
<h4 class="mt16">Nemo Enim Ipsam</h4>
<p>
Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium
doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore
veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim
ipsam voluptatem quia voluptas
</p>
<div class="text-center"> <a href="http://www.openerp.com/start" class="btn mt16 btn-lg btn-primary">Call To <em>Action</em></a></div>
</div>
</div>
</section>
</div>
<section class="container" data-snippet-id="resize">
<div class="row">
<div class="col-md-12 text-center">
<h2 class="mt48">Ut enim ad minim veniam</h2>
</div>
</div>
<div class="row mt32">
<div class="col-md-4">
<img class="img-rounded img-responsive" src="/website/static/src/img/china_thumb.jpg" />
<h4 class="mt16">Cillum Dolore</h4>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat.
</p>
</div>
<div class="col-md-4">
<img class="img-rounded img-responsive" src="/website/static/src/img/desert_thumb.jpg"/>
<h4 class="mt16">Mollit Anim</h4>
<p>
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum
dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
proident, sunt in culpa qui officia deserunt mollit anim id est laborum
</p>
</div>
<div class="col-md-4">
<img class="img-rounded img-responsive" src="/website/static/src/img/deers_thumb.jpg"/>
<h4 class="mt16">Nemo Enim Ipsam</h4>
<p>
Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium
doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore
veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim
ipsam voluptatem quia voluptas
</p>
</div>
</div>
</section>
</t>
</template>
@@ -416,99 +405,11 @@
</a>
</template>
<template id="snippets">
<div class='oe_snippet' data-snippet-id='darken' data-action='mutate' data-selector='#wrap .container' data-action-function='openerp.website.mutations.darken'>
<div class='oe_snippet_thumbnail oe_label'>Darken</div>
</div>
<div class='oe_snippet' data-snippet-id='surprise' data-action='mutate' data-selector='#wrap .container' data-action-function='openerp.website.mutations.vomify'>
<div class='oe_snippet_thumbnail oe_label' style='background-image:radial-gradient(red,orange,yellow,green,blue);'>Surprise!</div>
</div>
<div class='oe_snippet' data-snippet-id='spacer' data-action='insert' data-selector-siblings='#wrap .container'>
<div class='oe_snippet_thumbnail oe_label'>Spacer</div>
<div class="oe_snippet_body container mt32"></div>
</div>
<div class='oe_snippet' data-snippet-id='title' data-action='insert' data-selector-siblings='#wrap .container'>
<div class='oe_snippet_thumbnail oe_label'>
Main Title
</div>
<section class="oe_snippet_body container">
<div class="row">
<div class="col-md-12 text-center">
<h1 class="mt64">Welcome To Your New Homepage,</h1>
<h2 class="mt32 subheader">where <b><em>everything</em></b> can be customized </h2>
</div>
</div>
</section>
</div>
<div class='oe_snippet' data-snippet-id='' data-action='insert' data-selector-siblings='#wrap .container'>
<section class="oe_snippet_body container">
<div class="row">
<div class="col-md-12 text-center">
<h2 class="mt48">Ut enim ad minim veniam</h2>
</div>
</div>
<div class="row mt32">
<div class="col-md-4">
<img class="img-rounded img-responsive" src="/website/static/src/img/china_thumb.jpg"/>
<h4 class="mt16">Cillum Dolore</h4>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat.</p>
</div>
<div class="col-md-4">
<img class="img-rounded img-responsive" src="/website/static/src/img/desert_thumb.jpg"/>
<h4 class="mt16">Mollit Anim</h4>
<p>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum
dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
proident, sunt in culpa qui officia deserunt mollit anim id est laborum</p>
</div>
<div class="col-md-4">
<img class="img-rounded img-responsive" src="/website/static/src/img/deers_thumb.jpg"/>
<h4 class="mt16">Nemo Enim Ipsam</h4>
<p>Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium
doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore
veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim
ipsam voluptatem quia voluptas</p>
</div>
</div>
</section>
</div>
<div class='oe_snippet' name='Header' data-action='insert' data-selector-siblings='#wrap .container'>
<section class="container oe_snippet_body">
<div class="row">
<div class="col-md-5">
<img class="img-rounded img-responsive" src="/website/static/src/img/island.jpg"/>
</div>
<div class="col-md-6 col-lg-offset-1">
<p class="mt16">Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat.</p>
<p>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum
dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
proident, sunt in culpa qui officia deserunt mollit anim id est laborum</p>
<div class="text-center">
<a href="http://www.openerp.com/start" class="btn btn-default mt16 btn-lg btn-primary">Call To <em>Action</em></a>
</div>
</div>
</div>
</section>
</div>
</template>
<template id="kanban">
<t t-set="step"><t t-esc="step or 0"/></t>
<t t-set="scope"><t t-esc="scope or 0"/></t>
<t t-set="orderby"><t t-esc="orderby or 'name'"/></t>
<t t-raw="snipped['kanban'](model, domain, column, template, step=step, scope=scope, orderby=orderby)"/>
<t t-raw="website.kanban(model, domain, column, template, step=step, scope=scope, orderby=orderby)"/>
</template>
<template id="kanban_contain">
+66 -115
View File
@@ -5,7 +5,6 @@ import simplejson
import openerp
from openerp.osv import osv, fields
from openerp.addons.web import http
from openerp.addons.web.controllers import main
from openerp.addons.web.http import request
import urllib
import math
@@ -21,10 +20,10 @@ def route(*route_args, **route_kwargs):
@http.route(*route_args, **route_kwargs)
@functools.wraps(f, assigned=functools.WRAPPER_ASSIGNMENTS + ('func_name',))
def wrap(*args, **kwargs):
if not hasattr(request, 'webcontext'):
request.webcontext = WebContext()
request.context['lang'] = request.webcontext['lang_selected']['code']
request.route_lang = None # WIP: decorator will support lang argument
if not hasattr(request, 'website'):
request.website = request.registry['website'].get_current()
request.website.preprocess_request(*args, **kwargs)
return f(*args, **kwargs)
return wrap
return decorator
@@ -46,31 +45,15 @@ def urlplus(url, params):
url += "%s=%s&" % (k, urllib.quote_plus(str(v)))
return url
class WebsiteError(Exception):
pass
class WebContext(dict):
def __init__(self):
self.website = request.registry.get("website")
lang = request.httprequest.host.split('.')[0]
context = self.website.get_webcontext(lang=lang)
dict.__init__(self, context)
def __getattr__(self, name):
if hasattr(self.website, name):
return getattr(self.website, name)
elif name in self:
return self[name]
else:
raise AttributeError
def render(self, template, values=None):
context = self.copy()
if values:
context.update(values)
return self.website.render(template, context)
class website(osv.osv):
_name = "website" # Avoid website.website convention for conciseness (for new api). Got a special authorization from xmo and rco
_description = "Website"
_columns = {
'name': fields.char('Domain'),
'company_id': fields.many2one('res.company', string="Company"),
'language_ids': fields.many2many('res.lang', 'website_lang_rel', 'website_id', 'lang_id', 'Languages'),
'default_lang_id': fields.many2one('res.lang', string="Default language"),
}
public_user = None
@@ -80,100 +63,78 @@ class website(osv.osv):
self.public_user = request.registry[ref[0]].browse(request.cr, openerp.SUPERUSER_ID, ref[1])
return self.public_user
def get_lang_info(self, lang):
fields = ['id', 'name', 'code', 'website_default']
lang_obj = request.registry['res.lang']
languages = lang_obj.search_read(
request.cr, openerp.SUPERUSER_ID, [('website_activated', '=', True)], fields
)
activated = [lg['code'].lower() for lg in languages]
default = [lg['code'] for lg in languages if lg['website_default']]
default = default[0] if default else None
def get_lang(self):
website = request.registry['website'].get_current()
# Try to get the language from cookie
lang = lang or request.httprequest.cookies.get('lang', None)
if not lang or lang not in activated:
# Try to get the default language
if default:
lang = default
# Otherwise get the first activated language
elif activated:
lang = activated[0]
# Otherwise the language setup is broken
else:
raise WebsiteError("Could not aquire default language")
if hasattr(request, 'route_lang'):
lang = request.route_lang
else:
lang = request.params.get('lang', None) or request.httprequest.cookies.get('lang', None)
return {
'lang_list': languages,
'lang_default': default,
'lang_selected': (lg for lg in languages if lg['code'].lower() == lang.lower()).next(),
}
if lang not in [lg.code for lg in website.language_ids]:
lang = website.default_lang_id.code
def get_webcontext(self, additional_values=None, lang=None):
debug = 'debug' in request.params
is_logged = True
try:
request.session.check_security()
except: # TODO fme: check correct exception
is_logged = False
return lang
def preprocess_request(self, cr, uid, ids, *args, **kwargs):
is_public_user = request.uid == self.get_public_user().id
values = {
'debug': debug,
request.context.update({
'is_public_user': is_public_user,
'editable': is_logged and not is_public_user,
'request': request,
'registry': request.registry,
'cr': request.cr,
'uid': request.uid,
'host_url': request.httprequest.host_url,
'res_company': request.registry['res.company'].browse(request.cr, openerp.SUPERUSER_ID, 1),
'json': simplejson,
'snipped': {
'kanban': self.kanban
},
}
values.update(self.get_lang_info(lang))
'editable': not is_public_user, # TODO: check perms
})
request.context['lang'] = self.get_lang()
if additional_values:
values.update(additional_values)
return values
def get_current(self):
# WIP, currently hard coded
return self.browse(request.cr, request.uid, 1)
def render(self, template, values=None):
def render(self, cr, uid, ids, template, values=None):
view = request.registry.get("ir.ui.view")
IMD = request.registry.get("ir.model.data")
if not values:
qweb_context = request.context.copy()
if values is None:
values = {}
values.update({
'request': request,
'registry': request.registry,
'json': simplejson,
'website': request.website,
'res_company': request.website.company_id,
})
qweb_context.update(values)
context = {
'inherit_branding': values.get('editable', False),
'inherit_branding': qweb_context.get('editable', False),
}
# check if xmlid of the template exists
try:
model, xmlid = template.split('.', 1)
model, id = IMD.get_object_reference(request.cr, request.uid, model, xmlid)
model, id = IMD.get_object_reference(cr, uid, model, xmlid)
except ValueError:
logger.error("Website Rendering Error.\n\n%s" % traceback.format_exc())
return self.render('website.404', values)
return self.render('website.404', qweb_context)
# render template and catch error
try:
return view.render(request.cr, request.uid, template, values, context=context)
return view.render(cr, uid, template, qweb_context, context=context)
except (AccessError, AccessDenied), err:
logger.error(err)
values['error'] = err[1]
qweb_context['error'] = err[1]
logger.warn("Website Rendering Error.\n\n%s" % traceback.format_exc())
return self.render('website.401', values)
return self.render('website.401', qweb_context)
except Exception:
values['traceback'] = traceback.format_exc()
logger.error("Website Rendering Error.\n\n%s" % values['traceback'])
if values['editable']:
return view.render(request.cr, request.uid, 'website.500', values, context=context)
qweb_context['traceback'] = traceback.format_exc()
logger.error("Website Rendering Error.\n\n%s" % qweb_context['traceback'])
if qweb_context['editable']:
return view.render(cr, uid, 'website.500', qweb_context, context=context)
else:
return view.render(request.cr, request.uid, 'website.404', values, context=context)
return view.render(cr, uid, 'website.404', qweb_context, context=context)
def pager(self, url, total, page=1, step=30, scope=5, url_args=None):
def pager(self, cr, uid, ids, url, total, page=1, step=30, scope=5, url_args=None):
# Compute Pager
page_count = int(math.ceil(float(total) / step))
@@ -228,7 +189,7 @@ class website(osv.osv):
if xids[view['id']]
]
def kanban(self, model, domain, column, template, step=None, scope=None, orderby=None):
def kanban(self, cr, uid, ids, model, domain, column, template, step=None, scope=None, orderby=None):
step = step and int(step) or 10
scope = scope and int(scope) or 5
orderby = orderby or "name"
@@ -249,12 +210,12 @@ class website(osv.osv):
pages[int(col[0])] = int(col[1])
objects = []
for group in model_obj.read_group(request.cr, request.uid, domain, ["id", column], groupby=column):
for group in model_obj.read_group(cr, uid, domain, ["id", column], groupby=column):
obj = {}
# browse column
relation_id = group[column][0]
obj['column_id'] = relation_obj.browse(request.cr, request.uid, relation_id)
obj['column_id'] = relation_obj.browse(cr, uid, relation_id)
obj['kanban_url'] = kanban_url
for k, v in pages.items():
@@ -262,7 +223,7 @@ class website(osv.osv):
obj['kanban_url'] += "%s-%s" % (k, v)
# pager
number = model_obj.search(request.cr, request.uid, group['__domain'], count=True)
number = model_obj.search(cr, uid, group['__domain'], count=True)
obj['page_count'] = int(math.ceil(float(number) / step))
obj['page'] = pages.get(relation_id) or 1
if obj['page'] > obj['page_count']:
@@ -278,8 +239,8 @@ class website(osv.osv):
obj['orderby'] = orderby
# browse objects
object_ids = model_obj.search(request.cr, request.uid, group['__domain'], limit=step, offset=offset, order=orderby)
obj['object_ids'] = model_obj.browse(request.cr, request.uid, object_ids)
object_ids = model_obj.search(cr, uid, group['__domain'], limit=step, offset=offset, order=orderby)
obj['object_ids'] = model_obj.browse(cr, uid, object_ids)
objects.append(obj)
@@ -288,30 +249,20 @@ class website(osv.osv):
'range': range,
'template': template,
}
return request.webcontext.render("website.kanban_contain", values)
return request.website.render("website.kanban_contain", values)
def kanban_col(self, model, domain, page, template, step, orderby):
def kanban_col(self, cr, uid, ids, model, domain, page, template, step, orderby):
html = ""
model_obj = request.registry[model]
domain = safe_eval(domain)
step = int(step)
offset = (int(page)-1) * step
object_ids = model_obj.search(request.cr, request.uid, domain, limit=step, offset=offset, order=orderby)
object_ids = model_obj.browse(request.cr, request.uid, object_ids)
object_ids = model_obj.search(cr, uid, domain, limit=step, offset=offset, order=orderby)
object_ids = model_obj.browse(cr, uid, object_ids)
for object_id in object_ids:
html += request.webcontext.render(template, {'object_id': object_id})
html += request.website.render(template, {'object_id': object_id})
return html
class res_lang(osv.osv):
_inherit = "res.lang"
_columns = {
'website_activated': fields.boolean('Active on website'),
'website_default': fields.boolean('Website default language'),
}
# TODO: on write and create set website_default=False on other records if current is True
class res_partner(osv.osv):
_inherit = "res.partner"
+5 -29
View File
@@ -1,35 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="res_lang_tree" model="ir.ui.view">
<field name="name">res.lang.tree website</field>
<field name="model">res.lang</field>
<field name="inherit_id" ref="base.res_lang_tree"/>
<field name="arch" type="xml">
<field name="active" position="after">
<field name="website_activated"/>
<field name="website_default"/>
</field>
</field>
</record>
<record id="res_lang_form" model="ir.ui.view">
<field name="name">res.lang.form website</field>
<field name="model">res.lang</field>
<field name="inherit_id" ref="base.res_lang_form"/>
<field name="arch" type="xml">
<field name="active" position="after">
<field name="website_activated"/>
<field name="website_default"/>
</field>
</field>
</record>
</data>
<data noupdate="1">
<record id="base.lang_en" model="res.lang">
<field name="website_activated">True</field>
<field name="website_default">True</field>
<record id="default_website" model="website">
<field name="name">www.dundermufflin.com</field>
<field name="company_id" ref="base.main_company"/>
<field name="language_ids" eval="[(6, 0, [ ref('base.lang_en')])]"/>
<field name="default_lang_id" ref="base.lang_en"/>
</record>
<record id="public_user" model="res.users">
+64
View File
@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data noupdate="1">
<record id="default_website" model="website">
<field name="name">localhost</field>
<field name="company_id" ref="base.main_company"/>
<field name="language_ids" eval="[(6, 0, [ ref('base.lang_en')])]"/>
<field name="default_lang_id" ref="base.lang_en"/>
</record>
</data>
<data>
<record id="action_website_form" model="ir.actions.act_window">
<field name="name">Websites</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">website</field>
<field name="view_type">form</field>
<field name="help">Create and manage the companies websites.</field>
</record>
<menuitem action="action_website_form" id="menu_action_website_form" parent="base.menu_res_company_global"/>
<record id="view_website_tree" model="ir.ui.view">
<field name="name">website.tree</field>
<field name="model">website</field>
<field name="arch" type="xml">
<tree string="Websites">
<field name="name"/>
<field name="company_id" groups="base.group_multi_company"/>
<field name="default_lang_id"/>
</tree>
</field>
</record>
<record id="view_website_form" model="ir.ui.view">
<field name="name">website.form</field>
<field name="model">website</field>
<field name="arch" type="xml">
<form string="Website" version="7.0">
<sheet>
<div class="oe_title">
<label for="name" class="oe_edit_only"/>
<h1>
<field name="name" class="oe_inline"/>
</h1>
</div>
<group col="4">
<field name="company_id" groups="base.group_multi_company"/>
<field name="default_lang_id"/>
</group>
<group string="Website languages">
<field name="language_ids" nolabel="1" mode="tree,form">
<tree>
<field name="name"/>
<field name="code"/>
<field name="translatable"/>
</tree>
</field>
</group>
</sheet>
</form>
</field>
</record>
</data>
</openerp>
+48 -24
View File
@@ -11,63 +11,87 @@ class website_contract(http.Controller):
@website.route(['/references/', '/references/page/<int:page>/'], type='http', auth="public")
def references(self, page=0, **post):
website = request.registry['website']
partner_obj = request.registry['res.partner']
account_obj = request.registry['account.analytic.account']
# check contracts
contract_ids = account_obj.search(request.cr, openerp.SUPERUSER_ID, [(1, "=", 1)])
contract_project_ids = [contract.partner_id.id
for contract in account_obj.browse(request.cr, openerp.SUPERUSER_ID, contract_ids) if contract.partner_id]
contracts = account_obj.browse(request.cr, openerp.SUPERUSER_ID,
contract_ids, request.context)
contract_project_ids = [contract.partner_id.id for contract in contracts
if contract.partner_id]
domain = ['|', ('id', "in", contract_project_ids), ('id', "child_of", contract_project_ids)]
if post.get('search'):
domain += ['|',
('name', 'ilike', "%%%s%%" % post.get("search")),
('name', 'ilike', "%%%s%%" % post.get("search")),
('website_description', 'ilike', "%%%s%%" % post.get("search"))]
# public partner profile
partner_ids = partner_obj.search(request.cr, openerp.SUPERUSER_ID, domain + [('website_published', '=', True)])
partner_ids = partner_obj.search(
request.cr, openerp.SUPERUSER_ID,
domain + [('website_published', '=', True)], context=request.context)
google_map_partner_ids = ",".join([str(p) for p in partner_ids])
if not request.webcontext.is_public_user:
if not request.context['is_public_user']:
# search without website_published
partner_ids += partner_obj.search(request.cr, request.uid, domain)
partner_ids += partner_obj.search(request.cr, request.uid,
domain, context=request.context)
partner_ids = list(set(partner_ids))
# group by country
countries = partner_obj.read_group(request.cr, request.uid, domain, ["id", "country_id"], groupby="country_id", orderby="country_id")
countries.insert(0, {'country_id_count': partner_obj.search(request.cr, request.uid, domain, count=True), 'country_id': ("all", _("All Countries"))})
countries = partner_obj.read_group(
request.cr, request.uid, domain, ["id", "country_id"],
groupby="country_id", orderby="country_id", context=request.context)
country_count = partner_obj.search(
request.cr, request.uid, domain, count=True, context=request.context)
countries.insert(0, {
'country_id_count': country_count,
'country_id': ("all", _("All Countries"))
})
if post.get("country", "all") != 'all':
partner_ids = partner_obj.search(request.cr, request.uid, [('id', 'in', partner_ids), ('country_id', '=', int(post.get('country')))])
partner_ids = partner_obj.search(
request.cr, request.uid,
[
('id', 'in', partner_ids),
('country_id', '=', int(post.get('country')))
], context=request.context)
step = 20
pager = website.pager(url="/references/", total=len(partner_ids), page=page, step=step, scope=7, url_args=post)
partner_ids = partner_obj.search(request.cr, openerp.SUPERUSER_ID, [('id', 'in', partner_ids)], limit=step, offset=pager['offset'])
pager = request.website.pager(url="/references/", total=len(partner_ids), page=page, step=step, scope=7, url_args=post)
partner_ids = partner_obj.search(
request.cr, openerp.SUPERUSER_ID, [('id', 'in', partner_ids)],
limit=step, offset=pager['offset'], context=request.context)
partners = partner_obj.browse(request.cr, openerp.SUPERUSER_ID,
partner_ids, request.context)
values = {
'countries': countries,
'partner_ids': partner_obj.browse(request.cr, openerp.SUPERUSER_ID, partner_ids),
'partner_ids': partners,
'google_map_partner_ids': google_map_partner_ids,
'pager': pager,
'searches': post,
'search_path': "?%s" % urllib.urlencode(post),
}
return request.webcontext.render("website_contract.index", values)
return request.website.render("website_contract.index", values)
@website.route(['/references/<int:ref_id>/'], type='http', auth="public")
def references_ref(self, ref_id=0, **post):
partner_obj = request.registry['res.partner']
partner_ids = partner_obj.search(request.cr, openerp.SUPERUSER_ID, [('website_published', '=', True), ('id', '=', ref_id)])
if not request.webcontext.is_public_user:
partner_ids += partner_obj.search(request.cr, request.uid, [('id', '=', ref_id)])
partner_ids = partner_obj.search(
request.cr, openerp.SUPERUSER_ID, [
('website_published', '=', True),
('id', '=', ref_id)
], context=request.context)
if not request.context['is_public_user']:
partner_ids += partner_obj.search(
request.cr, request.uid, [('id', '=', ref_id)],
context=request.context)
values = {
'partner_id': partner_obj.browse(request.cr, openerp.SUPERUSER_ID, partner_ids[0], context={'show_address': True}),
'partner_id': partner_obj.browse(
request.cr, openerp.SUPERUSER_ID, partner_ids[0],
dict(request.context, show_address=True)),
}
return request.webcontext.render("website_contract.details", values)
return request.website.render("website_contract.details", values)
@@ -6,7 +6,7 @@
<!-- Layout add nav and footer -->
<template id="footer_custom" inherit_id="website.layout" name="Custom Footer">
<xpath expr="//body/footer//a[@href='/page/website.aboutus']" position="after">
<xpath expr="//body/footer//div[@name='info']/ul" position="inside">
<li><a href="/references/">References</a></li>
</xpath>
</template>
+4 -3
View File
@@ -11,11 +11,12 @@ class contactus(http.Controller):
@website.route(['/crm/contactus'], type='http', auth="admin")
def contactus(self, *arg, **post):
post['user_id'] = False
request.registry['crm.lead'].create(request.cr, request.uid, post)
company = request.webcontext['res_company']
request.registry['crm.lead'].create(request.cr, request.uid,
post, request.context)
company = request.website.company_id
values = {
'google_map_url': "http://maps.googleapis.com/maps/api/staticmap?center=%s&sensor=false&zoom=8&size=298x298" % quote_plus('%s, %s %s, %s' % (company.street, company.city, company.zip, company.country_id and company.country_id.name_get()[0][1] or ''))
}
return request.webcontext.render("website_crm.thanks", values)
return request.website.render("website_crm.thanks", values)
# vim:expandtab:tabstop=4:softtabstop=4:shiftwidth=4:
@@ -11,7 +11,6 @@ class website_crm_partner_assign(http.Controller):
@website.route(['/partners/', '/partners/page/<int:page>/'], type='http', auth="public")
def partners(self, page=0, **post):
website = request.registry['website']
partner_obj = request.registry['res.partner']
def dom_without(without):
@@ -26,54 +25,82 @@ class website_crm_partner_assign(http.Controller):
domain_search = {}
if post.get('search'):
domain_search["search"] += ['|',
('name', 'ilike', "%%%s%%" % post.get("search")),
('name', 'ilike', "%%%s%%" % post.get("search")),
('website_description', 'ilike', "%%%s%%" % post.get("search"))]
if post.get("grade", "all") != 'all':
domain_search["grade"] = [("grade_id", "=", int(post.get("grade")))]
if post.get("country", "all") != 'all':
domain_search["country"] = [("country_id", "=", int(post.get("country")))]
# public partner profile
partner_ids = partner_obj.search(request.cr, openerp.SUPERUSER_ID, dom_without(False) )
partner_ids = partner_obj.search(
request.cr, openerp.SUPERUSER_ID, dom_without(False),
context=request.context)
google_map_partner_ids = ",".join([str(p) for p in partner_ids])
# group by country
domain = dom_without("country")
countries = partner_obj.read_group(request.cr, request.uid, domain, ["id", "country_id"], groupby="country_id", orderby="country_id")
countries.insert(0, {'country_id_count': partner_obj.search(request.cr, request.uid, domain, count=True), 'country_id': ("all", _("All Countries"))})
countries = partner_obj.read_group(
request.cr, request.uid, domain, ["id", "country_id"],
groupby="country_id", orderby="country_id", context=request.context)
partners = partner_obj.search(request.cr, request.uid, domain,
context=request.context, count=True)
countries.insert(0, {
'country_id_count': partners,
'country_id': ("all", _("All Countries"))
})
# group by grade
domain = dom_without("grade")
grades = partner_obj.read_group(request.cr, request.uid, domain, ["id", "grade_id"], groupby="grade_id", orderby="grade_id")
grades.insert(0, {'grade_id_count': partner_obj.search(request.cr, request.uid, domain, count=True), 'grade_id': ("all", _("All Grade"))})
grades = partner_obj.read_group(
request.cr, request.uid, domain, ["id", "grade_id"],
groupby="grade_id", orderby="grade_id", context=request.context)
grade_id_count = partner_obj.search(request.cr, request.uid, domain,
count=True, context=request.context)
grades.insert(0, {
'grade_id_count': grade_id_count,
'grade_id': ("all", _("All Grade"))
})
step = 20
pager = website.pager(url="/partners/", total=len(partner_ids), page=page, step=step, scope=7, url_args=post)
partner_ids = partner_obj.search(request.cr, openerp.SUPERUSER_ID, [('id', 'in', partner_ids)],
limit=step, offset=pager['offset'], order="grade_id ASC,partner_weight DESC")
pager = request.website.pager(url="/partners/", total=len(partner_ids), page=page, step=step, scope=7, url_args=post)
partner_ids = partner_obj.search(
request.cr, openerp.SUPERUSER_ID, [('id', 'in', partner_ids)],
context=request.context, limit=step, offset=pager['offset'],
order="grade_id ASC,partner_weight DESC")
partners = partner_obj.browse(request.cr, openerp.SUPERUSER_ID,
partner_ids, request.context)
values = {
'countries': countries,
'grades': grades,
'partner_ids': partner_obj.browse(request.cr, openerp.SUPERUSER_ID, partner_ids),
'partner_ids': partners,
'google_map_partner_ids': google_map_partner_ids,
'pager': pager,
'searches': post,
'search_path': "?%s" % urllib.urlencode(post),
}
return request.webcontext.render("website_crm_partner_assign.index", values)
return request.website.render("website_crm_partner_assign.index", values)
@website.route(['/partners/<int:ref_id>/'], type='http', auth="public")
def partners_ref(self, ref_id=0, **post):
partner_obj = request.registry['res.partner']
partner_ids = partner_obj.search(request.cr, openerp.SUPERUSER_ID, [('website_published', '=', True), ('id', '=', ref_id)])
if not request.webcontext.is_public_user:
partner_ids += partner_obj.search(request.cr, request.uid, [('id', '=', ref_id)])
partner_ids = partner_obj.search(
request.cr, openerp.SUPERUSER_ID,
[('website_published', '=', True), ('id', '=', ref_id)],
context=request.context)
if not request.context['is_public_user']:
partner_ids += partner_obj.search(
request.cr, request.uid, [('id', '=', ref_id)],
context=request.context)
values = {
'partner_id': partner_obj.browse(request.cr, openerp.SUPERUSER_ID, partner_ids[0], context={'show_address': True}),
'partner_id': partner_obj.browse(
request.cr, openerp.SUPERUSER_ID, partner_ids[0],
context=dict(request.context, show_address=True)),
}
return request.webcontext.render("website_crm_partner_assign.details", values)
return request.website.render("website_crm_partner_assign.details", values)
@@ -6,7 +6,7 @@
<!-- Layout add nav and footer -->
<template id="footer_custom" inherit_id="website.layout" name="Custom Footer">
<xpath expr="//body/footer//a[@href='/page/website.aboutus']" position="after">
<xpath expr="//body/footer//div[@name='info']/ul" position="inside">
<li><a href="/partners/">Partners</a></li>
</xpath>
</template>
+98 -23
View File
@@ -18,7 +18,6 @@ class website_event(http.Controller):
@website.route(['/event', '/event/page/<int:page>/'], type='http', auth="public")
def events(self, page=1, **searches):
website = request.registry['website']
event_obj = request.registry['event.event']
searches.setdefault('date', 'all')
@@ -72,23 +71,45 @@ class website_event(http.Controller):
# count by domains without self search
for date in dates:
date[3] = event_obj.search(request.cr, request.uid, dom_without('date') + date[2], count=True)
date[3] = event_obj.search(
request.cr, request.uid, dom_without('date') + date[2],
count=True, context=request.context)
domain = dom_without('type')
types = event_obj.read_group(request.cr, request.uid, domain, ["id", "type"], groupby="type", orderby="type")
types.insert(0, {'type_count': event_obj.search(request.cr, request.uid, domain, count=True), 'type': ("all", _("All Categories"))})
types = event_obj.read_group(
request.cr, request.uid, domain, ["id", "type"], groupby="type",
orderby="type", context=request.context)
type_count = event_obj.search(request.cr, request.uid, domain,
count=True, context=request.context)
types.insert(0, {
'type_count': type_count,
'type': ("all", _("All Categories"))
})
domain = dom_without('country')
countries = event_obj.read_group(request.cr, request.uid, domain, ["id", "country_id"], groupby="country_id", orderby="country_id")
countries.insert(0, {'country_id_count': event_obj.search(request.cr, request.uid, domain, count=True), 'country_id': ("all", _("All Countries"))})
countries = event_obj.read_group(
request.cr, request.uid, domain, ["id", "country_id"],
groupby="country_id", orderby="country_id", context=request.context)
country_id_count = event_obj.search(request.cr, request.uid, domain,
count=True, context=request.context)
countries.insert(0, {
'country_id_count': country_id_count,
'country_id': ("all", _("All Countries"))
})
step = 5
event_count = event_obj.search(request.cr, request.uid, dom_without("none"), count=True)
pager = website.pager(url="/event/", total=event_count, page=page, step=step, scope=5)
obj_ids = event_obj.search(request.cr, request.uid, dom_without("none"), limit=step, offset=pager['offset'], order="date_begin DESC")
event_count = event_obj.search(
request.cr, request.uid, dom_without("none"), count=True,
context=request.context)
pager = request.website.pager(url="/event/", total=event_count, page=page, step=step, scope=5)
obj_ids = event_obj.search(
request.cr, request.uid, dom_without("none"), limit=step,
offset=pager['offset'], order="date_begin DESC", context=request.context)
events_ids = event_obj.browse(request.cr, request.uid, obj_ids,
context=request.context)
values = {
'event_ids': event_obj.browse(request.cr, request.uid, obj_ids),
'event_ids': events_ids,
'dates': dates,
'types': types,
'countries': countries,
@@ -97,16 +118,19 @@ class website_event(http.Controller):
'search_path': "?%s" % urllib.urlencode(searches),
}
return request.webcontext.render("website_event.index", values)
return request.website.render("website_event.index", values)
@website.route(['/event/<int:event_id>'], type='http', auth="public")
def event(self, event_id=None, **post):
event = request.registry['event.event'].browse(request.cr, request.uid, event_id, {'show_address': 1})
event_obj = request.registry['event.event']
values = {
'event_id': event,
'event_id': event_obj.browse(request.cr, request.uid, event_id,
dict(request.context, show_address=1)),
'message_ids': event_obj.browse(request.cr, request.uid, event_id, request.context).message_ids,
'subscribe': post.get('subscribe'),
'range': range
}
return request.webcontext.render("website_event.detail", values)
return request.website.render("website_event.detail", values)
@website.route(['/event/<int:event_id>/add_cart'], type='http', auth="public")
def add_cart(self, event_id=None, **post):
@@ -114,16 +138,16 @@ class website_event(http.Controller):
order_line_obj = request.registry.get('sale.order.line')
ticket_obj = request.registry.get('event.event.ticket')
order = request.webcontext['order']
order = request.context['website_sale_order']
if not order:
order = website_sale.controllers.main.get_order()
partner_id = user_obj.browse(request.cr, SUPERUSER_ID, request.uid).partner_id.id
context = {}
partner_id = user_obj.browse(request.cr, SUPERUSER_ID, request.uid,
context=request.context).partner_id.id
fields = [k for k, v in order_line_obj._columns.items()]
values = order_line_obj.default_get(request.cr, SUPERUSER_ID, fields, context=context)
values = order_line_obj.default_get(request.cr, SUPERUSER_ID, fields,
context=request.context)
_values = None
for key, value in post.items():
@@ -131,7 +155,8 @@ class website_event(http.Controller):
ticket_id = key.split("-")[0] == 'ticket' and int(key.split("-")[1]) or None
if not ticket_id or not quantity:
continue
ticket = ticket_obj.browse(request.cr, request.uid, ticket_id)
ticket = ticket_obj.browse(request.cr, request.uid, ticket_id,
context=request.context)
values['product_id'] = ticket.product_id.id
values['event_id'] = ticket.event_id.id
@@ -145,12 +170,62 @@ class website_event(http.Controller):
# change and record value
pricelist_id = order.pricelist_id and order.pricelist_id.id or False
_values = order_line_obj.product_id_change(request.cr, SUPERUSER_ID, [], pricelist_id, ticket.product_id.id, partner_id=partner_id, context=context)['value']
_values = order_line_obj.product_id_change(
request.cr, SUPERUSER_ID, [], pricelist_id, ticket.product_id.id,
partner_id=partner_id, context=request.context)['value']
_values.update(values)
order_line_id = order_line_obj.create(request.cr, SUPERUSER_ID, _values, context=context)
order.write({'order_line': [(4, order_line_id)]}, context=context)
order_line_id = order_line_obj.create(request.cr, SUPERUSER_ID,
_values, context=request.context)
order.write({'order_line': [(4, order_line_id)]}, context=request.context)
if not _values:
return werkzeug.utils.redirect("/event/%s/" % event_id)
return werkzeug.utils.redirect("/shop/checkout")
@website.route(['/event/<int:event_id>/subscribe'], type='http', auth="public")
def subscribe(self, event_id=None, **post):
partner_obj = request.registry['res.partner']
event_obj = request.registry['event.event']
user_obj = request.registry['res.users']
if event_id and 'subscribe' in post and (post.get('email') or not request.context['is_public_user']):
if request.context['is_public_user']:
partner_ids = partner_obj.search(
request.cr, SUPERUSER_ID, [("email", "=", post.get('email'))],
context=request.context)
if not partner_ids:
partner_data = {
"email": post.get('email'),
"name": "Subscribe: %s" % post.get('email')
}
partner_ids = [partner_obj.create(
request.cr, SUPERUSER_ID, partner_data, context=request.context)]
else:
partner_ids = [user_obj.browse(
request.cr, request.uid, request.uid,
context=request.context).partner_id.id]
event_obj.check_access_rule(request.cr, request.uid, [event_id],
'read', request.context)
event_obj.message_subscribe(request.cr, SUPERUSER_ID, [event_id],
partner_ids, request.context)
return self.event(event_id=event_id, subscribe=post.get('email'))
@website.route(['/event/<int:event_id>/unsubscribe'], type='http', auth="public")
def unsubscribe(self, event_id=None, **post):
partner_obj = request.registry['res.partner']
event_obj = request.registry['event.event']
user_obj = request.registry['res.users']
if event_id and 'unsubscribe' in post and (post.get('email') or not request.context['is_public_user']):
if request.context['is_public_user']:
partner_ids = partner_obj.search(
request.cr, SUPERUSER_ID, [("email", "=", post.get('email'))],
context=request.context)
else:
partner_ids = [user_obj.browse(request.cr, request.uid, request.uid, request.context).partner_id.id]
event_obj.check_access_rule(request.cr, request.uid, [event_id], 'read', request.context)
event_obj.message_unsubscribe(request.cr, SUPERUSER_ID, [event_id], partner_ids, request.context)
return self.event(event_id=event_id, subscribe=None)
-3
View File
@@ -18,9 +18,6 @@ class event(osv.osv):
'description_website': fields.html('Description for the website'),
}
class event_event(osv.osv):
_inherit = "event.event"
def google_map_img(self, cr, uid, ids, zoom=8, width=298, height=298, context=None):
partner = self.browse(cr, uid, ids[0], context=context)
if partner.address_id:
@@ -1,2 +0,0 @@
sass:
sass --compass --trace -t expanded hr.sass hr.css
@@ -1 +0,0 @@
@import "compass/css3"
+70 -57
View File
@@ -4,11 +4,11 @@
<!-- Layout add nav and footer -->
<template id="footer_custom" inherit_id="website.layout" name="Custom Footer">
<xpath expr="//body/header//ul[@id='top_menu']/li[last()]" position="before">
<template id="header_footer" inherit_id="website_sale.header_footer">
<xpath expr="//body/header//ul[@id='top_menu']/li" position="before">
<li><a href="/event">Events</a></li>
</xpath>
<xpath expr="//body/footer//a[@href='/page/website.contactus']" position="after">
<xpath expr="//body/footer//ul[@name='products']/li" position="after">
<li><a href="/event">Events</a></li>
</xpath>
</template>
@@ -26,7 +26,6 @@
<template id="index" name="Events" page="True">
<t t-call="website.layout">
<t t-set="head">
<script type="text/javascript" src="/website_hr/static/src/js/website_hr.js"></script>
<t t-raw="head or ''"/>
</t>
<t t-set="title">Events</t>
@@ -124,25 +123,28 @@
<t t-set="title">Events</t>
<div class="container">
<t t-call="website.publish"><t t-set="object" t-value="event_id"/></t>
<h1 class="text-center" t-field="event_id.name"></h1>
<span t-if="not event_id.event_ticket_ids" class="label label-default pull-left">No tickets needed.</span>
<t t-if="event_id.event_ticket_ids">
<span t-if="event_id.register_avail == 9999" class="label label-default pull-left">Tickets availables.</span>
<span t-if="not event_id.register_avail" class="label label-default pull-left">No ticket available.</span>
<span t-if="event_id.register_avail and event_id.register_avail != 9999" t-attf-class="label label-default pull-left label-#{ event_id.register_avail &lt;= 10 and 'warning' or 'info' }">
<t t-if="event_id.register_avail &lt;= 10">Only</t>
<t t-esc="event_id.register_avail"/>
<t t-if="event_id.register_avail &gt; 1">tickets </t>
<t t-if="event_id.register_avail == 1">ticket </t>
available.
</span>
</t>
<h4 class="text-center">
<i class="icon-time"></i> <span t-field="event_id.date_begin"/> to
<span t-field="event_id.date_end"/>
</h4>
<h5 class="text-center" t-field="event_id.address_id"/>
<div class="row">
<div class="col-md-4">
<form action="./subscribe" method="POST" class="form-inline" t-if="not subscribe">
<div class="col-lg-7">
<input placeholder="Email Address" type="email" name="email" class="form-control" t-if="is_public_user"/>
</div>
<button type="submit" class="btn btn-primary" name="subscribe">Subscribe</button>
</form>
<form action="./unsubscribe" method="POST" class="form-inline" t-if="subscribe">
<input type="hidden" name="email" t-att-value="subscribe"/>
<button type="submit" class="btn btn-default" name="unsubscribe">Unsubscribe</button>
</form>
</div>
<div class="col-md-8">
<h1 class="text-center" t-field="event_id.name"></h1>
<h4 class="text-center">
<i class="icon-time"></i> <span t-field="event_id.date_begin"/> to
<span t-field="event_id.date_end"/>
</h4>
<h5 class="text-center" t-field="event_id.address_id"/>
</div>
<div class="col-md-4 css_noprint">
<h4>When &amp; Where</h4>
<a t-att-href="event_id.google_map_link()" target="_BLANK">
@@ -159,41 +161,52 @@
</address>
</div>
<div class="col-md-8">
<div class="media-body">
<t t-if="event_id.event_ticket_ids">
<hr/>
<form t-attf-action="/event/#{ event_id.id }/add_cart" method="post">
<table class="table">
<thead>
<tr>
<th>Ticket</th>
<th>Sales End</th>
<th>Price</th>
<th>Seats</th>
<th>Quantity</th>
</tr>
</thead>
<tbody>
<tr t-foreach="event_id.event_ticket_ids" t-as="ticket">
<td t-field="ticket.name"/>
<td t-field="ticket.deadline"/>
<td t-field="ticket.price"/>
<td><span t-if="ticket.register_max"><t t-esc="ticket.register_current"/>/<t t-field="ticket.register_max"/></span></td>
<td>
<select t-if="ticket.register_avail" t-attf-name="ticket- #{ ticket.id }" class="form-control">
<t t-foreach="range(0, ticket.register_avail > 9 and 10 or ticket.register_avail+1 )" t-as="nb"><option t-esc="nb"/></t>
</select>
<span t-if="not ticket.register_avail">Not ticket available</span>
</td>
</tr>
</tbody>
</table>
<button type="submit" class="btn btn-primary" t-if="event_id.register_avail">Order Now</button>
</form>
</t>
<hr/>
<div t-field="event_id.description_website"></div>
</div>
<t t-if="event_id.event_ticket_ids">
<hr/>
<form t-attf-action="/event/#{ event_id.id }/add_cart" method="post">
<table class="table">
<thead>
<tr>
<th>Ticket</th>
<th>Sales End</th>
<th>Price</th>
<th>Seats</th>
<th>Quantity</th>
</tr>
</thead>
<tbody>
<tr t-foreach="event_id.event_ticket_ids" t-as="ticket">
<td t-field="ticket.name"/>
<td t-field="ticket.deadline"/>
<td t-field="ticket.price"/>
<td><span t-if="ticket.register_max"><t t-esc="ticket.register_current"/>/<t t-field="ticket.register_max"/></span></td>
<td>
<select t-if="ticket.register_avail" t-attf-name="ticket-#{ ticket.id }" class="form-control">
<t t-foreach="range(0, ticket.register_avail > 9 and 10 or ticket.register_avail+1 )" t-as="nb"><option t-esc="nb"/></t>
</select>
<span t-if="not ticket.register_avail">No tickets available</span>
</td>
</tr>
</tbody>
</table>
<button type="submit" class="btn btn-primary" t-if="event_id.register_avail">Order Now</button>
</form>
</t>
<hr/>
<div t-field="event_id.description_website"></div>
<hr/>
<ul class="media-list" id="comment">
<li t-foreach="message_ids" t-as="comment" class="media">
<div class="media-body">
<t t-call="website.publish"><t t-set="object" t-value="comment"/></t>
<t t-raw="comment.body"/>
<small class="pull-right muted text-right">
<div t-field="comment.author_id"/>
<div t-field="comment.date"/>
</small>
</div>
</li>
</ul>
</div>
</div>
</div>
+13 -8
View File
@@ -16,19 +16,23 @@ class google_map(http.Controller):
'height': post.get('height', 460),
'partner_url': post.get('partner_url'),
}
return request.webcontext.render("website_google_map.google_map", values)
return request.website.render("website_google_map.google_map", values)
@website.route(['/google_map/partners.json'], type='http', auth="admin")
def google_map_data(self, *arg, **post):
partner_obj = request.registry['res.partner']
domain = [("id", "in", [int(p) for p in post.get('partner_ids', "").split(",") if p])]
partner_ids = partner_obj.search(request.cr, openerp.SUPERUSER_ID, domain + [('website_published', '=', True)])
if not request.webcontext.is_public_user:
partner_ids += partner_obj.search(request.cr, request.uid, domain)
domain_public = domain + [('website_published', '=', True)]
partner_ids = partner_obj.search(request.cr, openerp.SUPERUSER_ID,
domain_public, context=request.context)
if not request.context['is_public_user']:
partner_ids += partner_obj.search(request.cr, request.uid, domain,
context=request.context)
partner_ids = list(set(partner_ids))
return partner_obj.google_map_json(request.cr, openerp.SUPERUSER_ID, partner_ids)
return partner_obj.google_map_json(request.cr, openerp.SUPERUSER_ID,
partner_ids, request.context)
@website.route(['/google_map/set_partner_position/'], type='http', auth="admin")
def google_map_set_partner_position(self, *arg, **post):
@@ -38,13 +42,14 @@ class google_map(http.Controller):
latitude = post.get('latitude') and float(post['latitude'])
longitude = post.get('longitude') and float(post['longitude'])
if not request.webcontext.is_public_user and partner_id and (latitude or longitude):
if not request.context['is_public_user'] and partner_id and (latitude or longitude):
values = {
'partner_latitude': latitude,
'partner_longitude': longitude,
'date_localization': datetime.now().strftime('%Y-%m-%d'),
}
partner_obj.write(request.cr, request.uid, [partner_id], values)
}
partner_obj.write(request.cr, request.uid, [partner_id], values,
request.context)
# vim:expandtab:tabstop=4:softtabstop=4:shiftwidth=4:
+5 -3
View File
@@ -9,8 +9,10 @@ class website_hr(http.Controller):
@website.route(['/page/website.aboutus'], type='http', auth="public")
def blog(self, **post):
hr_obj = request.registry['hr.employee']
employee_ids = hr_obj.search(request.cr, request.uid, [(1, "=", 1)])
employee_ids = hr_obj.search(request.cr, request.uid, [(1, "=", 1)],
context=request.context)
values = {
'employee_ids': hr_obj.browse(request.cr, request.uid, employee_ids)
'employee_ids': hr_obj.browse(request.cr, request.uid, employee_ids,
request.context)
}
return request.webcontext.render("website.aboutus", values)
return request.website.render("website.aboutus", values)
@@ -1,2 +0,0 @@
sass:
sass --compass --trace -t expanded hr.sass hr.css
-1
View File
@@ -1 +0,0 @@
@import "compass/css3"
+1 -2
View File
@@ -13,7 +13,7 @@
<!-- Layout add nav and footer -->
<template id="footer_custom" inherit_id="website.layout" name="Custom Footer">
<xpath expr="//body/footer//a[@href='/page/website.aboutus']" position="after">
<xpath expr="//body/footer//div[@name='info']/ul" position="inside">
<li><a href="/blog/%(website_hr.website_mail_job)d/">Jobs</a></li>
</xpath>
</template>
@@ -24,7 +24,6 @@
<template id="aboutus" inherit_id="website.aboutus">
<xpath expr="//div[@class='col-md-8']" position="inside">
<t t-set="head">
<script type="text/javascript" src="/website_hr/static/src/js/website_hr.js"></script>
<t t-raw="head or ''"/>
</t>
<h3>Our team</h3>
+28 -33
View File
@@ -17,7 +17,6 @@ class website_mail(http.Controller):
@website.route(['/blog/', '/blog/<int:mail_group_id>/', '/blog/<int:mail_group_id>/<int:blog_id>/',
'/blog/page/<int:page>/', '/blog/<int:mail_group_id>/page/<int:page>/', '/blog/<int:mail_group_id>/<int:blog_id>/page/<int:page>/'], type='http', auth="public")
def blog(self, mail_group_id=None, blog_id=None, page=0, **post):
website = request.registry['website']
group_obj = request.registry['mail.group']
message_obj = request.registry['mail.message']
user_obj = request.registry['res.users']
@@ -28,17 +27,16 @@ class website_mail(http.Controller):
'nav_list': dict(),
'mail_group_id': mail_group_id,
'subscribe': post.get('subscribe'),
'website': website,
}
if not request.webcontext.is_public_user and mail_group_id:
message_follower_ids = group_obj.read(request.cr, request.uid, [mail_group_id], ['message_follower_ids'])[0]['message_follower_ids']
parent_id = user_obj.browse(request.cr, SUPERUSER_ID, request.uid).partner_id.id
if not request.context['is_public_user'] and mail_group_id:
message_follower_ids = group_obj.read(request.cr, request.uid, [mail_group_id], ['message_follower_ids'], request.context)[0]['message_follower_ids']
parent_id = user_obj.browse(request.cr, SUPERUSER_ID, request.uid, request.context).partner_id.id
values['subscribe'] = parent_id in message_follower_ids
domain = mail_group_id and [("res_id", "=", mail_group_id)] or []
for group in message_obj.read_group(request.cr, request.uid, domain + group_obj.get_domain_public_blog(request.cr, request.uid), ['subject', 'date'], groupby="date", orderby="create_date asc"):
for group in message_obj.read_group(request.cr, request.uid, domain + group_obj.get_domain_public_blog(request.cr, request.uid), ['subject', 'date'], groupby="date", orderby="create_date asc", context=request.context):
year = group['date'].split(" ")[1]
if not values['nav_list'].get(year):
values['nav_list'][year] = {'name': year, 'date_count': 0, 'months': []}
@@ -46,29 +44,28 @@ class website_mail(http.Controller):
values['nav_list'][year]['months'].append(group)
if blog_id:
values['blog_id'] = message_obj.browse(request.cr, request.uid, blog_id)
values['blog_id'] = message_obj.browse(request.cr, request.uid, blog_id, request.context)
else:
step = 20
message_count = len(group_obj.get_public_message_ids(request.cr, request.uid, domain=domain, order="create_date desc"))
pager = website.pager(url="/blog/%s/" % mail_group_id, total=message_count, page=page, step=step, scope=7)
message_ids = group_obj.get_public_message_ids(request.cr, request.uid, domain=domain, order="create_date desc", limit=step, offset=pager['offset'])
message_count = len(group_obj.get_public_message_ids(request.cr, request.uid, domain=domain, order="create_date desc", context=request.context))
pager = request.website.pager(url="/blog/%s/" % mail_group_id, total=message_count, page=page, step=step, scope=7)
message_ids = group_obj.get_public_message_ids(request.cr, request.uid, domain=domain, order="create_date desc", limit=step, offset=pager['offset'], context=request.context)
values['pager'] = pager
values['blog_ids'] = message_obj.browse(request.cr, request.uid, message_ids)
values['blog_ids'] = message_obj.browse(request.cr, request.uid, message_ids, request.context)
return request.webcontext.render("website_mail.index", values)
return request.website.render("website_mail.index", values)
@website.route(['/blog/nav'], type='http', auth="public")
def nav(self, **post):
comment_ids = request.registry['mail.group'].get_public_message_ids(request.cr, request.uid, domain=safe_eval(post.get('domain')), order="create_date asc", limit=None)
return simplejson.dumps(request.registry['mail.message'].read(request.cr, request.uid, comment_ids, ['website_published', 'subject', 'res_id']))
comment_ids = request.registry['mail.group'].get_public_message_ids(request.cr, request.uid, domain=safe_eval(post.get('domain')), order="create_date asc", limit=None, context=request.context)
return simplejson.dumps(request.registry['mail.message'].read(request.cr, request.uid, comment_ids, ['website_published', 'subject', 'res_id'], request.context))
@website.route(['/blog/<int:mail_group_id>/<int:blog_id>/post'], type='http', auth="public")
def blog_post(self, mail_group_id=None, blog_id=None, **post):
website = request.registry['website']
url = request.httprequest.host_url
if post.get('body'):
request.session.body = post.get('body')
if request.webcontext.is_public_user:
if request.context['is_public_user']:
return '%s/admin#action=redirect&url=%s/blog/%s/%s/post' % (url, url, mail_group_id, blog_id)
if 'body' in request.session and request.session.body:
@@ -78,8 +75,7 @@ class website_mail(http.Controller):
website_published=blog_id and True or False,
type='comment',
subtype='mt_comment',
context={'mail_create_nosubsrequest.cribe': True},
)
context=dict(request.context, mail_create_nosubcribe=True))
request.session.body = False
if post.get('body'):
@@ -95,8 +91,7 @@ class website_mail(http.Controller):
website_published=False,
type='comment',
subtype='mt_comment',
context={'mail_create_nosubsrequest.cribe': True},
)
context=dict(request.context, mail_create_nosubscribe=True))
return werkzeug.utils.redirect("/blog/%s/%s/" % (mail_group_id, blog_id))
@website.route(['/blog/<int:mail_group_id>/subscribe', '/blog/<int:mail_group_id>/<int:blog_id>/subscribe'], type='http', auth="public")
@@ -105,15 +100,15 @@ class website_mail(http.Controller):
group_obj = request.registry['mail.group']
user_obj = request.registry['res.users']
if mail_group_id and 'subscribe' in post and (post.get('email') or not request.webcontext.is_public_user):
if request.webcontext.is_public_user:
partner_ids = partner_obj.search(request.cr, SUPERUSER_ID, [("email", "=", post.get('email'))])
if mail_group_id and 'subscribe' in post and (post.get('email') or not request.context['is_public_user']):
if request.context['is_public_user']:
partner_ids = partner_obj.search(request.cr, SUPERUSER_ID, [("email", "=", post.get('email'))], context=request.context)
if not partner_ids:
partner_ids = [partner_obj.create(request.cr, SUPERUSER_ID, {"email": post.get('email'), "name": "Subscribe: %s" % post.get('email')})]
partner_ids = [partner_obj.create(request.cr, SUPERUSER_ID, {"email": post.get('email'), "name": "Subscribe: %s" % post.get('email')}, request.context)]
else:
partner_ids = [user_obj.browse(request.cr, request.uid, request.uid).partner_id.id]
group_obj.check_access_rule(request.cr, request.uid, [mail_group_id], 'read')
group_obj.message_subscribe(request.cr, SUPERUSER_ID, [mail_group_id], partner_ids)
partner_ids = [user_obj.browse(request.cr, request.uid, request.uid, request.context).partner_id.id]
group_obj.check_access_rule(request.cr, request.uid, [mail_group_id], 'read', request.context)
group_obj.message_subscribe(request.cr, SUPERUSER_ID, [mail_group_id], partner_ids, request.context)
return self.blog(mail_group_id=mail_group_id, blog_id=blog_id, subscribe=post.get('email'))
@@ -123,12 +118,12 @@ class website_mail(http.Controller):
group_obj = request.registry['mail.group']
user_obj = request.registry['res.users']
if mail_group_id and 'unsubscribe' in post and (post.get('email') or not request.webcontext.is_public_user):
if request.webcontext.is_public_user:
partner_ids = partner_obj.search(request.cr, SUPERUSER_ID, [("email", "=", post.get('email'))])
if mail_group_id and 'unsubscribe' in post and (post.get('email') or not request.context['is_public_user']):
if request.context['is_public_user']:
partner_ids = partner_obj.search(request.cr, SUPERUSER_ID, [("email", "=", post.get('email'))], context=request.context)
else:
partner_ids = [user_obj.browse(request.cr, request.uid, request.uid).partner_id.id]
group_obj.check_access_rule(request.cr, request.uid, [mail_group_id], 'read')
group_obj.message_unsubscribe(request.cr, SUPERUSER_ID, [mail_group_id], partner_ids)
partner_ids = [user_obj.browse(request.cr, request.uid, request.uid, request.context).partner_id.id]
group_obj.check_access_rule(request.cr, request.uid, [mail_group_id], 'read', request.context)
group_obj.message_unsubscribe(request.cr, SUPERUSER_ID, [mail_group_id], partner_ids, request.context)
return self.blog(mail_group_id=mail_group_id, blog_id=blog_id, subscribe=None)
-3
View File
@@ -27,9 +27,6 @@ class mail_message(osv.osv):
_columns = {
'website_published': fields.boolean('Publish', help="Publish on the website as a blog"),
}
_defaults = {
'website_published': lambda *args: True
}
class mail_group(osv.Model):
+9 -7
View File
@@ -12,7 +12,7 @@
<!-- Layout add nav and footer -->
<template id="footer_custom" inherit_id="website.layout" name="Custom Footer">
<template id="header_footer_custom" inherit_id="website.layout">
<xpath expr="//body/header//ul[@id='top_menu']/li[last()]" position="before">
<li><a href="/blog/%(website_mail.website_mail_blog)d/">News</a></li>
</xpath>
@@ -35,9 +35,11 @@
<div class="row">
<div class="col-md-4" id="left_column">
<a t-if="mail_group_id and editable" t-attf-href="/blog/#{ mail_group_id }/new" class="btn btn-default">Add a new Blog</a>
<div id="blog_subscribe">
<div>
<form action="./subscribe" method="POST" class="form-inline" t-if="not subscribe">
<input placeholder="Email Address" type="email" name="email" class="input-medium" t-if="is_public_user"/>
<div class="col-lg-7">
<input placeholder="Email Address" type="email" name="email" class="form-control" t-if="is_public_user"/>
</div>
<button type="submit" class="btn btn-primary" name="subscribe">Subscribe</button>
</form>
<form action="./unsubscribe" method="POST" class="form-inline" t-if="subscribe">
@@ -68,11 +70,11 @@
</small>
</div>
</li>
<form id="post" t-attf-action="/blog/#{ blog_id.res_id }/#{ blog_id.id }/post#post" method="POST" class="form-horizontal text-center" groups="group_website_mail_reply">
<textarea rows="4" placeholder="Your comment" class="col-md-7 form-control"></textarea>
<button type="submit" class="btn btn-default">Post your comment</button>
</form>
</ul>
<form id="post" t-attf-action="/blog/#{ blog_id.res_id }/#{ blog_id.id }/post#post" method="POST" class="form-horizontal text-center" groups="group_website_mail_reply">
<textarea rows="4" placeholder="Your comment" class="col-md-7 form-control"></textarea>
<button type="submit" class="btn btn-default">Post your comment</button>
</form>
</div>
</div>
+19 -20
View File
@@ -11,7 +11,6 @@ class website_crm_partner_assign(http.Controller):
@website.route(['/members/', '/members/page/<int:page>/'], type='http', auth="public")
def members(self, page=0, **post):
website = request.registry['website']
membership_obj = request.registry['membership.membership_line']
def dom_without(without):
@@ -25,48 +24,48 @@ class website_crm_partner_assign(http.Controller):
domain_search = {}
if post.get('search'):
domain_search["search"] += ['|',
('name', 'ilike', "%%%s%%" % post.get("search")),
('name', 'ilike', "%%%s%%" % post.get("search")),
('website_description', 'ilike', "%%%s%%" % post.get("search"))]
if post.get("membership_id", "all") != 'all':
domain_search["membership"] = [("membership", "=", int(post.get("membership")))]
# public membership
membership_ids = membership_obj.search(request.cr, openerp.SUPERUSER_ID, dom_without(False) )
memberships = membership_obj.browse(request.cr, openerp.SUPERUSER_ID, membership_ids )
membership_ids = membership_obj.search(request.cr, openerp.SUPERUSER_ID, dom_without(False), context=request.context)
memberships = membership_obj.browse(request.cr, openerp.SUPERUSER_ID, membership_ids, request.context)
google_map_partner_ids = ",".join([str(m.partner.id) for m in memberships])
# group by membership_id
domain = dom_without("membership")
memberships = membership_obj.read_group(request.cr, request.uid, domain, ["id", "membership_id"], groupby="membership_id", orderby="membership_id")
memberships.insert(0, {'membership_id_count': membership_obj.search(request.cr, request.uid, domain, count=True), 'membership_id': ("all", _("All Groups"))})
memberships = membership_obj.read_group(request.cr, request.uid, domain, ["id", "membership_id"], groupby="membership_id", orderby="membership_id", context=request.context)
memberships.insert(0, {'membership_id_count': membership_obj.search(request.cr, request.uid, domain, count=True, context=request.context), 'membership_id': ("all", _("All Groups"))})
step = 20
pager = website.pager(url="/members/", total=len(membership_ids), page=page, step=step, scope=7, url_args=post)
membership_ids = membership_obj.search(request.cr, openerp.SUPERUSER_ID, [('id', 'in', membership_ids)],
limit=step, offset=pager['offset'], order="membership_id ASC,date DESC")
pager = request.website.pager(url="/members/", total=len(membership_ids), page=page, step=step, scope=7, url_args=post)
membership_ids = membership_obj.search(
request.cr, openerp.SUPERUSER_ID, [('id', 'in', membership_ids)],
limit=step, offset=pager['offset'], order="membership_id ASC,date DESC",
context=request.context)
values = {
'memberships': memberships,
'membership_line_ids': membership_obj.browse(request.cr, openerp.SUPERUSER_ID, membership_ids),
'membership_line_ids': membership_obj.browse(request.cr, openerp.SUPERUSER_ID, membership_ids, request.context),
'google_map_partner_ids': google_map_partner_ids,
'pager': pager,
'searches': post,
'search_path': "?%s" % urllib.urlencode(post),
}
return request.webcontext.render("website_membership.index", values)
return request.website.render("website_membership.index", values)
@website.route(['/members/<int:ref_id>/'], type='http', auth="public")
def partners_ref(self, ref_id=0, **post):
partner_obj = request.registry['res.partner']
partner_ids = partner_obj.search(request.cr, openerp.SUPERUSER_ID, [('website_published', '=', True), ('id', '=', ref_id)])
if not request.webcontext.is_public_user:
partner_ids += partner_obj.search(request.cr, request.uid, [('id', '=', ref_id)])
partner_ids = partner_obj.search(request.cr, openerp.SUPERUSER_ID, [('website_published', '=', True), ('id', '=', ref_id)], context=request.context)
if not request.context['is_public_user']:
partner_ids += partner_obj.search(request.cr, request.uid, [('id', '=', ref_id)], context=request.context)
values = {
'partner_id': partner_obj.browse(request.cr, openerp.SUPERUSER_ID, partner_ids[0], context={'show_address': True}),
'partner_id': partner_obj.browse(
request.cr, openerp.SUPERUSER_ID, partner_ids[0],
context=dict(request.context, show_address=True)),
}
return request.webcontext.render("website_membership.details", values)
return request.website.render("website_membership.details", values)
@@ -6,7 +6,7 @@
<!-- Layout add nav and footer -->
<template id="footer_custom" inherit_id="website.layout" name="Custom Footer">
<xpath expr="//body/footer//a[@href='/page/website.aboutus']" position="after">
<xpath expr="//body/footer//div[@name='info']/ul" position="inside">
<li><a href="/members/">Members</a></li>
</xpath>
</template>
+8 -8
View File
@@ -8,13 +8,13 @@ from openerp.addons.website import website
class Website(osv.osv):
_inherit = "website"
def get_webcontext(self, values={}, **kw):
def preprocess_request(self, cr, uid, ids, *args, **kwargs):
project_obj = request.registry['project.project']
project_ids = project_obj.search(request.cr, request.uid, [('privacy_visibility', "=", "public")])
values.update({
'project_ids': project_obj.browse(request.cr, request.uid, project_ids),
})
return super(Website, self).get_webcontext(values, **kw)
project_ids = project_obj.search(cr, uid, [('privacy_visibility', "=", "public")], context=request.context)
request.context['website_project_ids'] = project_obj.browse(cr, uid, project_ids, request.context)
return super(Website, self).preprocess_request(cr, uid, ids, *args, **kwargs)
class website_project(http.Controller):
@@ -22,5 +22,5 @@ class website_project(http.Controller):
@website.route(['/project/<int:project_id>/'], type='http', auth="public")
def blog(self, project_id=None, **post):
project_obj = request.registry['project.project']
project = project_obj.browse(request.cr, request.uid, project_id)
return request.webcontext.render("website_project.index", {'project_id': project})
project = project_obj.browse(request.cr, request.uid, project_id, request.context)
return request.website.render("website_project.index", {'project_id': project})
@@ -6,8 +6,8 @@
<!-- Layout add nav and footer -->
<template id="footer_custom" inherit_id="website.layout" name="Custom Footer">
<xpath expr="//body/footer//div[@name='info']/ul" position="inside">
<li t-foreach="project_ids" t-as="project"><a t-attf-href="/project/#{ project.id }/"><t t-esc="project.name"/></a></li>
<xpath expr="//body/footer//ul[@name='products']" position="inside">
<li t-foreach="website_project_ids" t-as="project"><a t-attf-href="/project/#{ project.id }/"><t t-esc="project.name"/></a></li>
</xpath>
</template>
+65 -63
View File
@@ -15,25 +15,23 @@ def get_order(order_id=None):
context = {}
if order_id:
try:
order = order_obj.browse(request.cr, SUPERUSER_ID, order_id)
order = order_obj.browse(request.cr, SUPERUSER_ID, order_id, request.context)
order.pricelist_id
except:
order_id = None
if not order_id:
fields = [k for k, v in order_obj._columns.items()]
order_value = order_obj.default_get(request.cr, SUPERUSER_ID, fields)
order_value = order_obj.default_get(request.cr, SUPERUSER_ID, fields, request.context)
if request.httprequest.session.get('ecommerce_pricelist'):
order_value['pricelist_id'] = request.httprequest.session['ecommerce_pricelist']
order_value['partner_id'] = request.registry.get('res.users').browse(request.cr, SUPERUSER_ID, request.uid).partner_id.id
order_value.update(order_obj.onchange_partner_id(request.cr, SUPERUSER_ID, [], order_value['partner_id'], context={})['value'])
order_id = order_obj.create(request.cr, SUPERUSER_ID, order_value)
order = order_obj.browse(request.cr, SUPERUSER_ID, order_id)
order_value['partner_id'] = request.registry.get('res.users').browse(request.cr, SUPERUSER_ID, request.uid, request.context).partner_id.id
order_value.update(order_obj.onchange_partner_id(request.cr, SUPERUSER_ID, [], order_value['partner_id'], context=request.context)['value'])
order_id = order_obj.create(request.cr, SUPERUSER_ID, order_value, request.context)
order = order_obj.browse(request.cr, SUPERUSER_ID, order_id, request.context)
request.httprequest.session['ecommerce_order_id'] = order.id
context = {
'pricelist': order.pricelist_id.id,
}
return order_obj.browse(request.cr, SUPERUSER_ID, order_id, context=context)
return order_obj.browse(request.cr, SUPERUSER_ID, order_id,
context=dict(request.context, pricelist=order.pricelist_id.id))
def get_current_order():
if request.httprequest.session.get('ecommerce_order_id'):
@@ -43,12 +41,11 @@ def get_current_order():
class Website(osv.osv):
_inherit = "website"
def get_webcontext(self, values={}, **kw):
values.update({
'order': get_current_order(),
# 'website_sale_get_current_order': get_current_order, # TODO: replace 'order' key in templates
def preprocess_request(self, cr, uid, ids, *args, **kwargs):
request.context.update({
'website_sale_order': get_current_order(),
})
return super(Website, self).get_webcontext(values, **kw)
return super(Website, self).preprocess_request(cr, uid, ids, *args, **kwargs)
class Ecommerce(http.Controller):
@@ -63,8 +60,6 @@ class Ecommerce(http.Controller):
if 'promo' in post:
self.change_pricelist(post.get('promo'))
website = request.registry['website']
product_obj = request.registry.get('product.template')
domain = [("sale_ok", "=", True)]
@@ -82,21 +77,21 @@ class Ecommerce(http.Controller):
domain += [('product_variant_ids.public_categ_id.id', 'child_of', cat_id)] + domain
step = 20
product_count = len(product_obj.search(request.cr, request.uid, domain))
pager = website.pager(url="/shop/category/%s/" % cat_id, total=product_count, page=page, step=step, scope=7, url_args=post)
product_count = len(product_obj.search(request.cr, request.uid, domain, context=request.context))
pager = request.website.pager(url="/shop/category/%s/" % cat_id, total=product_count, page=page, step=step, scope=7, url_args=post)
product_ids = product_obj.search(request.cr, request.uid, domain, limit=step, offset=pager['offset'])
product_ids = product_obj.search(request.cr, request.uid, domain, limit=step, offset=pager['offset'], context=request.context)
context = {'pricelist': self.get_pricelist()}
request.context['pricelist'] = self.get_pricelist()
values = {
'categories': self.get_categories(),
'category_id': cat_id,
'products': product_obj.browse(request.cr, SUPERUSER_ID, product_ids, context=context),
'products': product_obj.browse(request.cr, SUPERUSER_ID, product_ids, context=request.context),
'search': post.get("search"),
'pager': pager,
}
return request.webcontext.render("website_sale.products", values)
return request.website.render("website_sale.products", values)
@website.route(['/shop/product/<product_id>/'], type='http', auth="public")
def product(self, cat_id=0, product_id=0, **post):
@@ -108,12 +103,13 @@ class Ecommerce(http.Controller):
product_obj = request.registry.get('product.template')
category_obj = request.registry.get('product.public.category')
context = {'pricelist': self.get_pricelist()}
category_list = category_obj.name_get(request.cr, request.uid, category_obj.search(request.cr, request.uid, [(1,'=',1)]))
category_ids = category_obj.search(request.cr, request.uid, [(1, '=', 1)], context=request.context)
category_list = category_obj.name_get(request.cr, request.uid, category_ids, request.context)
category_list = sorted(category_list, key=lambda category: category[1])
product = product_obj.browse(request.cr, request.uid, product_id, context=context)
request.context['pricelist'] = self.get_pricelist()
product = product_obj.browse(request.cr, request.uid, product_id, context=request.context)
values = {
'category_id': post.get('category_id') and int(post.get('category_id')) or None,
'search': post.get("search"),
@@ -121,16 +117,19 @@ class Ecommerce(http.Controller):
'category_list': category_list,
'product': product,
}
return request.webcontext.render("website_sale.product", values)
return request.website.render("website_sale.product", values)
@website.route(['/shop/add_product/'], type='http', auth="public")
def add_product(self, **post):
product_id = request.registry.get('product.product').create(request.cr, request.uid, {'name': 'New Product'})
return werkzeug.utils.redirect("/shop/product/%s/" % product_id)
@website.route(['/shop/add_product/', '/shop/category/<cat_id>/add_product/'], type='http', auth="public")
def add_product(self, cat_id=0, **post):
product_id = request.registry.get('product.product').create(request.cr, request.uid,
{'name': 'New Product', 'public_categ_id': cat_id}, request.context)
return werkzeug.utils.redirect("/shop/product/%s/?unable_editor=1" % product_id)
@website.route(['/shop/change_category/<product_id>/'], type='http', auth="public")
def edit_product(self, product_id=0, **post):
request.registry.get('product.template').write(request.cr, request.uid, [int(product_id)], {'public_categ_id': int(post.get('public_categ_id', 0))})
request.registry.get('product.template').write(
request.cr, request.uid, [int(product_id)],
{'public_categ_id': int(post.get('public_categ_id', 0))}, request.context)
return "1"
def get_pricelist(self):
@@ -144,13 +143,13 @@ class Ecommerce(http.Controller):
pricelist_id = False
if code:
pricelist_obj = request.registry.get('product.pricelist')
pricelist_ids = pricelist_obj.search(request.cr, SUPERUSER_ID, [('code', '=', code)])
pricelist_ids = pricelist_obj.search(request.cr, SUPERUSER_ID, [('code', '=', code)], context=request.context)
if pricelist_ids:
pricelist_id = pricelist_ids[0]
if not pricelist_id:
partner_id = request.registry.get('res.users').browse(request.cr, SUPERUSER_ID, request.uid).partner_id.id
pricelist_id = request.registry['sale.order'].onchange_partner_id(request.cr, SUPERUSER_ID, [], partner_id, context={})['value']['pricelist_id']
partner_id = request.registry.get('res.users').browse(request.cr, SUPERUSER_ID, request.uid, request.context).partner_id.id
pricelist_id = request.registry['sale.order'].onchange_partner_id(request.cr, SUPERUSER_ID, [], partner_id, context=request.context)['value']['pricelist_id']
request.httprequest.session['ecommerce_pricelist'] = pricelist_id
@@ -171,7 +170,7 @@ class Ecommerce(http.Controller):
if not order:
order = get_order()
context = {'pricelist': self.get_pricelist()}
context = dict(request.context, pricelist=self.get_pricelist())
quantity = 0
@@ -230,7 +229,7 @@ class Ecommerce(http.Controller):
if order:
for line in order.order_line:
suggested_ids += [p.id for p in line.product_id and line.product_id.suggested_product_ids or [] for line in order.order_line]
suggested_ids = prod_obj.search(request.cr, request.uid, [('id', 'in', suggested_ids)])
suggested_ids = prod_obj.search(request.cr, request.uid, [('id', 'in', suggested_ids)], context=request.context)
# select 3 random products
suggested_products = []
while len(suggested_products) < 3 and suggested_ids:
@@ -239,9 +238,9 @@ class Ecommerce(http.Controller):
values = {
'categories': self.get_categories(),
'suggested_products': prod_obj.browse(request.cr, request.uid, suggested_products),
'suggested_products': prod_obj.browse(request.cr, request.uid, suggested_products, request.context),
}
return request.webcontext.render("website_sale.mycart", values)
return request.website.render("website_sale.mycart", values)
@website.route(['/shop/<path:path>/add_cart/', '/shop/add_cart/'], type='http', auth="public")
def add_cart(self, path=None, product_id=None, order_line_id=None, remove=None, json=None):
@@ -285,16 +284,16 @@ class Ecommerce(http.Controller):
}
checkout = {}
if not request.webcontext.is_public_user:
partner = user_obj.browse(request.cr, request.uid, request.uid).partner_id
if not request.context['is_public_user']:
partner = user_obj.browse(request.cr, request.uid, request.uid, request.context).partner_id
partner_id = partner.id
fields = ["name", "phone", "fax", "company", "email", "street", "city", "state_id", "zip", "country_id"]
checkout = user_obj.read(request.cr, SUPERUSER_ID, [partner_id], fields)[0]
checkout = user_obj.read(request.cr, SUPERUSER_ID, [partner_id], fields, request.context)[0]
checkout['company'] = partner.parent_id and partner.parent_id.name or ''
shipping_ids = partner_obj.search(request.cr, request.uid, [("parent_id", "=", partner_id), ('type', "=", 'delivery')])
shipping_ids = partner_obj.search(request.cr, request.uid, [("parent_id", "=", partner_id), ('type', "=", 'delivery')], context=request.context)
if shipping_ids:
for k,v in partner_obj.read(request.cr, request.uid, shipping_ids[0]).items():
for k,v in partner_obj.read(request.cr, request.uid, shipping_ids[0], request.context).items():
checkout['shipping_'+k] = v or ''
checkout.update(request.session.setdefault('checkout', {}))
@@ -302,10 +301,12 @@ class Ecommerce(http.Controller):
checkout[k] = v or ''
values['checkout'] = checkout
values['countries'] = country_obj.browse(request.cr, SUPERUSER_ID, country_obj.search(request.cr, SUPERUSER_ID, [(1, "=", 1)]))
values['states'] = country_state_obj.browse(request.cr, SUPERUSER_ID, country_state_obj.search(request.cr, SUPERUSER_ID, [(1, "=", 1)]))
countries_ids = country_obj.search(request.cr, SUPERUSER_ID, [(1, "=", 1)], context=request.context)
values['countries'] = country_obj.browse(request.cr, SUPERUSER_ID, countries_ids, request.context)
states_ids = country_state_obj.search(request.cr, SUPERUSER_ID, [(1, "=", 1)], context=request.context)
values['states'] = country_state_obj.browse(request.cr, SUPERUSER_ID, states_ids, request.context)
return request.webcontext.render("website_sale.checkout", values)
return request.website.render("website_sale.checkout", values)
@website.route(['/shop/confirm_order/'], type='http', auth="public")
def confirm_order(self, **post):
@@ -335,10 +336,10 @@ class Ecommerce(http.Controller):
# search or create company
company_id = None
if post['company']:
company_ids = partner_obj.search(request.cr, SUPERUSER_ID, [("name", "ilike", post['company']), ('is_company', '=', True)])
company_ids = partner_obj.search(request.cr, SUPERUSER_ID, [("name", "ilike", post['company']), ('is_company', '=', True)], context=request.context)
company_id = company_ids and company_ids[0] or None
if not company_id:
company_id = partner_obj.create(request.cr, SUPERUSER_ID, {'name': post['company'], 'is_company': True})
company_id = partner_obj.create(request.cr, SUPERUSER_ID, {'name': post['company'], 'is_company': True}, request.context)
partner_value = {
'fax': post['fax'],
@@ -352,14 +353,14 @@ class Ecommerce(http.Controller):
'country_id': post['country_id'],
'state_id': post['state_id'],
}
if not request.webcontext.is_public_user:
partner_id = user_obj.browse(request.cr, request.uid, request.uid).partner_id.id
partner_obj.write(request.cr, request.uid, [partner_id], partner_value)
if not request.context['is_public_user']:
partner_id = user_obj.browse(request.cr, request.uid, request.uid, request.context).partner_id.id
partner_obj.write(request.cr, request.uid, [partner_id], partner_value, request.context)
else:
partner_id = partner_obj.create(request.cr, SUPERUSER_ID, partner_value)
partner_id = partner_obj.create(request.cr, SUPERUSER_ID, partner_value, request.context)
shipping_id = None
if 'shipping_name' in post:
if post.get('shipping_different'):
shipping_value = {
'fax': post['shipping_fax'],
'phone': post['shipping_phone'],
@@ -372,21 +373,22 @@ class Ecommerce(http.Controller):
'country_id': post['shipping_country_id'],
'state_id': post['shipping_state_id'],
}
domain = [(key, '_id' in key and '=' or 'ilike', '_id' in key and int(value) or value)
domain = [(key, '_id' in key and '=' or 'ilike', '_id' in key and value and int(value) or False)
for key, value in shipping_value.items() if key in required_field + ["type", "parent_id"]]
shipping_ids = partner_obj.search(request.cr, SUPERUSER_ID, domain)
shipping_ids = partner_obj.search(request.cr, SUPERUSER_ID, domain, context=request.context)
if shipping_ids:
shipping_id = shipping_ids[0]
partner_obj.write(request.cr, SUPERUSER_ID, [shipping_id], shipping_value)
partner_obj.write(request.cr, SUPERUSER_ID, [shipping_id], shipping_value, request.context)
else:
shipping_id = partner_obj.create(request.cr, SUPERUSER_ID, shipping_value)
shipping_id = partner_obj.create(request.cr, SUPERUSER_ID, shipping_value, request.context)
order_value = {
'partner_id': partner_id,
'partner_invoice_id': partner_id,
'partner_shipping_id': shipping_id or partner_id
}
order_value.update(request.registry.get('sale.order').onchange_partner_id(request.cr, SUPERUSER_ID, [], order.partner_id.id, context={})['value'])
order_value.update(request.registry.get('sale.order').onchange_partner_id(request.cr, SUPERUSER_ID, [], order.partner_id.id, context=request.context)['value'])
order.write(order_value)
return werkzeug.utils.redirect("/shop/payment/")
@@ -404,13 +406,13 @@ class Ecommerce(http.Controller):
}
payment_obj = request.registry.get('portal.payment.acquirer')
payment_ids = payment_obj.search(request.cr, SUPERUSER_ID, [('visible', '=', True)])
values['payments'] = payment_obj.browse(request.cr, SUPERUSER_ID, payment_ids)
payment_ids = payment_obj.search(request.cr, SUPERUSER_ID, [('visible', '=', True)], context=request.context)
values['payments'] = payment_obj.browse(request.cr, SUPERUSER_ID, payment_ids, request.context)
for payment in values['payments']:
content = payment_obj.render(request.cr, SUPERUSER_ID, payment.id, order, order.name, order.pricelist_id.currency_id, order.amount_total)
payment._content = content
return request.webcontext.render("website_sale.payment", values)
return request.website.render("website_sale.payment", values)
@website.route(['/shop/payment_validate/'], type='http', auth="public")
def payment_validate(self, **post):
@@ -16,19 +16,23 @@ $(document).ready(function () {
});
function set_my_cart_quantity(qty) {
$("#my_cart_quantity").html(qty.toString().indexOf(".") > -1 ? qty : qty + '.0');
$(".my_cart_quantity").html(qty.toString().indexOf(".") > -1 ? qty : qty + '.0').removeClass("hidden");
}
$(".oe_website_sale .oe_mycart input.js_quantity").change(function () {
var $input = $(this);
var value = parseInt($input.val());
if (!isNaN(value)) {
$.get("./set_cart/", {'order_line_id': $(this).data('id'), 'set_number': value, 'json': true}, function (data) {
var data = JSON.parse(data);
set_my_cart_quantity(data[1]);
$input.val(data[0]);
});
}
if (isNaN(value)) value = 0;
$.get("./set_cart/", {'order_line_id': $input.data('id'), 'set_number': value, 'json': true}, function (data) {
var data = JSON.parse(data);
set_my_cart_quantity(data[1]);
$input.val(data[0]);
if (!+data[0]) {
$.get('/shop/remove_cart/', {'order_line_id': $input.data('id'), 'json': true,}, function (data) {
location.reload();
});
}
});
});
$(".oe_website_sale #product_detail select[name='public_categ_id']").change(function () {
@@ -43,6 +47,9 @@ $(document).ready(function () {
$link = $(ev.currentTarget);
$.get($link.attr("href"), {'json': true}, function (data) {
var data = JSON.parse(data);
if (!+data[0]) {
location.reload();
}
set_my_cart_quantity(data[1]);
$link.parent().find(".js_quantity").val(data[0]);
});
+62 -55
View File
@@ -42,13 +42,24 @@
<!-- Layout add nav and footer -->
<template id="footer_custom" inherit_id="website.layout" name="Custom Footer">
<template id="header_footer" inherit_id="website.layout" name="Custom Footer">
<xpath expr="//body/header//ul[@id='top_menu']/li" position="before">
<li><a href="/shop/">Shop</a></li>
<li>
<a href="/shop/mycart/">
<i class="icon-shopping-cart"></i>
My cart <span id="my_cart_quantity" t-attf-class="badge #{(not order or not order.get_total_quantity()) and 'hidden' or ''}" t-esc="order and order.get_total_quantity() or ''"/>
My cart <span t-attf-class="my_cart_quantity badge #{(not website_sale_order or not website_sale_order.get_total_quantity()) and 'hidden' or ''}"
t-esc="website_sale_order and website_sale_order.get_total_quantity() or ''"/>
</a>
</li>
</xpath>
<xpath expr="//body/footer//ul[@name='products']" position="inside">
<li><a href="/shop/">Shop</a></li>
<li>
<a href="/shop/mycart/">
<i class="icon-shopping-cart"></i>
My cart <span t-attf-class="my_cart_quantity badge #{(not website_sale_order or not website_sale_order.get_total_quantity()) and 'hidden' or ''}"
t-esc="website_sale_order and website_sale_order.get_total_quantity() or ''"/>
</a>
</li>
</xpath>
@@ -64,7 +75,6 @@
</t>
<t t-set="title">Shop - <t t-raw="title">Categories</t></t>
<div class="container oe_website_sale">
<a t-if="editable" href="/shop/add_product/" class="btn btn-default pull-right">Create New Product</a>
<div class="row">
<div class="col-md-12" id="shop_content">
<t t-raw="shop_content" />
@@ -112,6 +122,8 @@
</t>
<t t-set="title">Product</t>
<t t-set="shop_content">
<a t-if="editable" t-attf-href="/shop/#{ category_id and ('category/%s/' % category_id) or ''}add_product/" class="btn btn-default pull-right">Create New Product</a>
<div t-if="editable" class="clearfix"/>
<t t-call="website.pager">
<t t-set="classname">pull-left</t>
</t>
@@ -172,26 +184,21 @@
</label><br/>
<button class="btn btn-sm btn-success">Add to basket</button>
</form>
<img class="media-object img-responsive" t-att-src="product.img('image_medium')"/>
<div t-if="editable" t-att-class="'well css_non_editable_mode_hidden'">
<h2>Category<br/><small>Select the public category</small></h2>
<select name="public_categ_id" class="form-control" t-att-data-id="product.id">
<option value="0">Choose a Category</option>
<t t-foreach="category_list" t-as="cat">
<option t-att-value="cat[0]" t-att-selected="cat[0] == product.public_categ_id.id and 'selected'"><t t-esc="cat[1]"/></option>
</t>
</select>
<h2>Short Description for Quotations<br/><small>Displayed in your shop categories and for quotations</small></h2>
<div class="form-control" t-field="product.description_sale">Enter a short description for this product</div>
</div>
<div t-field="product.website_description">Enter a short Website description for this product</div>
<div>
<div class="pull-right">
<t t-if="product.product_variant_ids[0].lst_price != product.product_variant_ids[0].price">
<h5 class="text-error" style="text-decoration: line-through;" title="Price without reduction"><t t-field="product.product_variant_ids[0].lst_price" /> €</h5>
<h4><t t-esc="product.product_variant_ids[0].price" /> €</h4>
</t>
<h4 t-if="product.product_variant_ids[0].lst_price == product.product_variant_ids[0].price"><t t-field="product.product_variant_ids[0].lst_price" /> €</h4>
</div>
<img class="media-object img-responsive" t-att-src="product.img('image_medium')"/>
<div t-field="product.website_description">
<div class="container mt32"></div>
<div class="container" data-snippet-id="container">
Replace this text with your Website product description
</div>
<div class="container mt32"></div>
</div>
</div>
</t>
</t>
@@ -238,11 +245,11 @@
<th>Quantity</th>
</tr>
</thead>
<tbody t-if="not order or not order.order_line">
<tbody t-if="not website_sale_order or not website_sale_order.order_line">
<tr><td colspan="4"><h3>Your cart is empty</h3></td></tr>
</tbody>
<tbody t-if="order and order.order_line">
<t t-foreach="order.order_line" t-as="line">
<tbody t-if="website_sale_order and website_sale_order.order_line">
<t t-foreach="website_sale_order.order_line" t-as="line">
<tr>
<td colspan="2" t-if="not line.product_id.product_tmpl_id"></td>
<td t-if="line.product_id.product_tmpl_id">
@@ -277,12 +284,12 @@
<col width="160"/>
</colgroup>
<thead>
<tr> <th colspan="3">Subtotal </th> <th><t t-esc="order and order.amount_untaxed or 0"/> €</th></tr>
<tr> <th colspan="3">Taxes </th> <th><t t-esc="order and order.amount_tax or 0"/> €</th></tr>
<tr> <th colspan="3"><h4>Total</h4></th> <th><h4><t t-esc="order and order.amount_total or 0"/> €</h4></th></tr>
<tr> <th colspan="3">Subtotal </th> <th><t t-esc="website_sale_order and website_sale_order.amount_untaxed or 0"/> €</th></tr>
<tr> <th colspan="3">Taxes </th> <th><t t-esc="website_sale_order and website_sale_order.amount_tax or 0"/> €</th></tr>
<tr> <th colspan="3"><h4>Total</h4></th> <th><h4><t t-esc="website_sale_order and website_sale_order.amount_total or 0"/> €</h4></th></tr>
</thead>
</table>
<a t-if="order and order.order_line" href="/shop/checkout/" class="btn btn-success">Proceed To Payment</a>
<a t-if="website_sale_order and website_sale_order.order_line" href="/shop/checkout/" class="btn btn-success">Proceed To Payment</a>
</div>
</t>
</t>
@@ -342,8 +349,8 @@
</template>
<template id="reduction_code" inherit_option_id="website_sale.mycart" name="Reduction Code">
<xpath expr="//table[@id='mycart_total']" position="after">
<form t-if="order and order.order_line" class="well" action="/shop/mycart/" method="post">
<input name="promo" class='input' type="text" placeholder="Reduction Code..." t-att-value="order.pricelist_id.code or ''"/>
<form t-if="website_sale_order and website_sale_order.order_line" class="well" action="/shop/mycart/" method="post">
<input name="promo" class='input' type="text" placeholder="Reduction Code..." t-att-value="website_sale_order.pricelist_id.code or ''"/>
<button class="btn">Apply Code</button>
</form>
</xpath>
@@ -370,8 +377,8 @@
<th>Qty.</th>
</tr>
</thead>
<tbody t-if="order.order_line">
<t t-foreach="order.order_line" t-as="line">
<tbody t-if="website_sale_order.order_line">
<t t-foreach="website_sale_order.order_line" t-as="line">
<tr>
<td><t t-esc="line.name"/></td>
<td><t t-esc="line.product_uom_qty"/></td>
@@ -380,59 +387,59 @@
</t>
</tbody>
<tfoot>
<tr><th>Untaxed Amount</th><td></td><td><t t-esc="order.amount_untaxed"/> €</td></tr>
<tr><th>Taxes</th><td></td><td><t t-esc="order.amount_tax"/> €</td></tr>
<tr><th>Total</th><td></td><td><t t-esc="order.amount_total"/> €</td></tr>
<tr><th>Untaxed Amount</th><td></td><td><t t-esc="website_sale_order.amount_untaxed"/> €</td></tr>
<tr><th>Taxes</th><td></td><td><t t-esc="website_sale_order.amount_tax"/> €</td></tr>
<tr><th>Total</th><td></td><td><t t-esc="website_sale_order.amount_total"/> €</td></tr>
</tfoot>
</table>
</div>
<form class="col-md-8 form-horizontal" action="/shop/confirm_order/" method="post">
<div class=" row">
<a t-if="not partner" t-attf-href="/admin#action=redirect&amp;url=#{ host_url }/shop/checkout/" class="btn btn-primary">Log me, I have an account</a>
<a t-if="not partner" t-attf-href="/admin#action=redirect&amp;url=#{ request.httprequest.host_url }/shop/checkout/" class="btn btn-primary">Log me, I have an account</a>
<h3 class="col-md-10">Billing Information</h3>
<div t-attf-class="form-group #{error.get('name', '')}">
<div t-attf-class="form-group #{error.get('name') and 'has-error' or ''}">
<label class="col-lg-3 control-label" for="contact_name">Name and firstname</label>
<div class="col-lg-4">
<input type="text" name="name" class="form-control" t-att-value="checkout.get('name', '')"/>
</div>
</div>
<div t-attf-class="form-group #{error.get('phone', '')}">
<div t-attf-class="form-group #{ error.get('phone') and 'has-error' or ''}">
<label class="col-lg-3 control-label" for="contact_name">Telephone</label>
<div class="col-lg-4">
<input type="tel" name="phone" class="form-control" t-att-value="checkout.get('phone', '')"/>
</div>
</div>
<div t-attf-class="form-group #{error.get('fax', '')}">
<div t-attf-class="form-group #{error.get('fax') and 'has-error' or ''}">
<label class="col-lg-3 control-label" for="contact_name">Fax</label>
<div class="col-lg-4">
<input type="tel" name="fax" class="form-control" t-att-value="checkout.get('fax', '')"/>
</div>
</div>
<div t-attf-class="form-group #{error.get('company', '')}">
<div t-attf-class="form-group #{error.get('company') and 'has-error' or ''}">
<label class="col-lg-3 control-label" for="contact_name">Company</label>
<div class="col-lg-4">
<input type="text" name="company" class="form-control" t-att-value="checkout.get('company', '')"/>
</div>
</div>
<div t-attf-class="form-group #{error.get('email', '')}">
<div t-attf-class="form-group #{error.get('email') and 'has-error' or ''}">
<label class="col-lg-3 control-label" for="contact_name">Email address</label>
<div class="col-lg-4">
<input type="email" name="email" class="form-control" t-att-value="checkout.get('email', '')"/>
</div>
</div>
<div t-attf-class="form-group #{error.get('street', '')}">
<div t-attf-class="form-group #{error.get('street') and 'has-error' or ''}">
<label class="col-lg-3 control-label" for="contact_name">Street</label>
<div class="col-lg-4">
<input type="text" name="street" class="form-control" t-att-value="checkout.get('street', '')"/>
</div>
</div>
<div t-attf-class="form-group #{error.get('city', '')}">
<div t-attf-class="form-group #{error.get('city') and 'has-error' or ''}">
<label class="col-lg-3 control-label" for="contact_name">City</label>
<div class="col-lg-4">
<input type="text" name="city" class="form-control" t-att-value="checkout.get('city', '')"/>
</div>
</div>
<div t-attf-class="form-group #{error.get('state_id', '')}">
<div t-attf-class="form-group #{error.get('state_id') and 'has-error' or ''}">
<label class="col-lg-3 control-label" for="contact_name">State / Province</label>
<div class="col-lg-4">
<select name="state_id" class="form-control">
@@ -443,13 +450,13 @@
</select>
</div>
</div>
<div t-attf-class="form-group #{error.get('zip', '')}">
<div t-attf-class="form-group #{error.get('zip') and 'has-error' or ''}">
<label class="col-lg-3 control-label" for="contact_name">Zip / Postal Code</label>
<div class="col-lg-4">
<input type="text" name="zip" class="form-control" t-att-value="checkout.get('zip', '')"/>
</div>
</div>
<div t-attf-class="form-group #{error.get('country_id', '')}">
<div t-attf-class="form-group #{error.get('country_id') and 'has-error' or ''}">
<label class="col-lg-3 control-label" for="contact_name">Country</label>
<div class="col-lg-4">
<select name="country_id" class="form-control">
@@ -472,37 +479,37 @@
</div>
<div class="js_shipping row" t-att-style="not shipping and 'display:none' or ''">
<h3 class="col-md-12 oe_shipping">Shipping Information</h3>
<div t-attf-class="form-group #{error.get('shipping_name', '')}">
<div t-attf-class="form-group #{error.get('shipping_name') and 'has-error' or ''}">
<label class="col-lg-3 control-label" for="contact_name">Name and firstname</label>
<div class="col-lg-4">
<input type="text" name="shipping_name" class="form-control" t-att-value="checkout.get('shipping_name', '')"/>
</div>
</div>
<div t-attf-class="form-group #{error.get('shipping_phone', '')}">
<div t-attf-class="form-group #{error.get('shipping_phone') and 'has-error' or ''}">
<label class="col-lg-3 control-label" for="contact_name">Telephone</label>
<div class="col-lg-4">
<input type="tel" name="shipping_phone" class="form-control" t-att-value="checkout.get('shipping_phone', '')"/>
</div>
</div>
<div t-attf-class="form-group #{error.get('shipping_fax', '')}">
<div t-attf-class="form-group #{error.get('shipping_fax') and 'has-error' or ''}">
<label class="col-lg-3 control-label" for="contact_name">Fax</label>
<div class="col-lg-4">
<input type="tel" name="shipping_fax" class="form-control" t-att-value="checkout.get('shipping_fax', '')"/>
</div>
</div>
<div t-attf-class="form-group #{error.get('shipping_street', '')}">
<div t-attf-class="form-group #{error.get('shipping_street') and 'has-error' or ''}">
<label class="col-lg-3 control-label" for="contact_name">Street</label>
<div class="col-lg-4">
<input type="text" name="shipping_street" class="form-control" t-att-value="checkout.get('shipping_street', '')"/>
</div>
</div>
<div t-attf-class="form-group #{error.get('shipping_city', '')}">
<div t-attf-class="form-group #{error.get('shipping_city') and 'has-error' or ''}">
<label class="col-lg-3 control-label" for="contact_name">City</label>
<div class="col-lg-4">
<input type="text" name="shipping_city" class="form-control" t-att-value="checkout.get('shipping_city', '')"/>
</div>
</div>
<div t-attf-class="form-group #{error.get('shipping_state_id', '')}">
<div t-attf-class="form-group #{error.get('shipping_state_id') and 'has-error' or ''}">
<label class="col-lg-3 control-label" for="contact_name">State / Province</label>
<div class="col-lg-4">
<select name="shipping_state_id" class="form-control">
@@ -513,13 +520,13 @@
</select>
</div>
</div>
<div t-attf-class="form-group #{error.get('shipping_zip', '')}">
<div t-attf-class="form-group #{error.get('shipping_zip') and 'has-error' or ''}">
<label class="col-lg-3 control-label" for="contact_name">Zip / Postal Code</label>
<div class="col-lg-4">
<input type="text" name="shipping_zip" class="form-control" t-att-value="checkout.get('shipping_zip', '')"/>
</div>
</div>
<div t-attf-class="form-group #{error.get('shipping_country_id', '')}">
<div t-attf-class="form-group #{error.get('shipping_country_id') and 'has-error' or ''}">
<label class="col-lg-3 control-label" for="contact_name">Country</label>
<div class="col-lg-4">
<select name="shipping_country_id" class="form-control">
@@ -556,8 +563,8 @@
<th>Qty.</th>
</tr>
</thead>
<tbody t-if="order.order_line">
<t t-foreach="order.order_line" t-as="line">
<tbody t-if="website_sale_order.order_line">
<t t-foreach="website_sale_order.order_line" t-as="line">
<tr>
<td><t t-esc="line.name"/></td>
<td><t t-esc="line.product_uom_qty"/></td>
@@ -566,9 +573,9 @@
</t>
</tbody>
<tfoot>
<tr><th>Untaxed Amount</th><td></td><td><t t-esc="order.amount_untaxed"/> €</td></tr>
<tr><th>Taxes</th><td></td><td><t t-esc="order.amount_tax"/> €</td></tr>
<tr><th>Total</th><td></td><td><t t-esc="order.amount_total"/> €</td></tr>
<tr><th>Untaxed Amount</th><td></td><td><t t-esc="website_sale_order.amount_untaxed"/> €</td></tr>
<tr><th>Taxes</th><td></td><td><t t-esc="website_sale_order.amount_tax"/> €</td></tr>
<tr><th>Total</th><td></td><td><t t-esc="website_sale_order.amount_total"/> €</td></tr>
</tfoot>
</table>
</div>
+1 -1
View File
@@ -94,7 +94,7 @@ class sale_order_line(osv.osv):
def _recalculate_product_values(self, cr, uid, ids, product_id=None, context=None):
user_obj = self.pool.get('res.users')
product_id = product_id or ids and self.browse(cr, uid, ids[0], context=context).product_id.id
return self.product_id_change(cr, uid, [],
return self.product_id_change(cr, SUPERUSER_ID, [],
pricelist=context.pop('pricelist'),
product=product_id,
partner_id=user_obj.browse(cr, SUPERUSER_ID, uid).partner_id.id,