[MERGE] Merge parents
bzr revid: rim@openerp.com-20131126130141-lu5r2gy5h5l28irv
This commit is contained in:
commit
fe139acc4d
|
@ -50,10 +50,9 @@ class Website(openerp.addons.web.controllers.main.Home):
|
|||
def pagenew(self, path, noredirect=NOPE):
|
||||
web = request.registry['website']
|
||||
try:
|
||||
path = web.new_page(request.cr, request.uid, path, request.context)
|
||||
path = web.new_page(request.cr, request.uid, path, context=request.context)
|
||||
except psycopg2.IntegrityError:
|
||||
logger.exception('Unable to create ir_model_data for page %s', path)
|
||||
request.cr.execute('ROLLBACK TO SAVEPOINT pagenew')
|
||||
return werkzeug.exceptions.InternalServerError()
|
||||
url = "/page/" + path
|
||||
if noredirect is not NOPE:
|
||||
|
@ -91,7 +90,13 @@ class Website(openerp.addons.web.controllers.main.Home):
|
|||
values = {
|
||||
'path': page,
|
||||
}
|
||||
|
||||
try:
|
||||
request.website.get_template(page)
|
||||
except (Exception), e:
|
||||
if request.context['editable']:
|
||||
page = 'website.page_404'
|
||||
else:
|
||||
return request.registry['ir.http']._handle_404(e)
|
||||
return request.website.render(page, values)
|
||||
|
||||
@website.route('/website/customize_template_toggle', type='json', auth='user')
|
||||
|
@ -227,7 +232,7 @@ class Website(openerp.addons.web.controllers.main.Home):
|
|||
obj = _object.browse(request.cr, request.uid, _id)
|
||||
return bool(obj.website_published)
|
||||
|
||||
@website.route(['/website/kanban/'], type='http', auth="public")
|
||||
@website.route(['/website/kanban/'], type='http', auth="public", methods=['POST'])
|
||||
def kanban(self, **post):
|
||||
return request.website.kanban_col(**post)
|
||||
|
||||
|
@ -239,7 +244,9 @@ class Website(openerp.addons.web.controllers.main.Home):
|
|||
|
||||
@website.route('/sitemap', type='http', auth='public', multilang=True)
|
||||
def sitemap(self):
|
||||
return request.website.render('website.sitemap', {'pages': request.website.enumerate_pages()})
|
||||
return request.website.render('website.sitemap', {
|
||||
'pages': request.website.enumerate_pages()
|
||||
})
|
||||
|
||||
@website.route('/sitemap.xml', type='http', auth="public")
|
||||
def sitemap_xml(self):
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import traceback
|
||||
import werkzeug.routing
|
||||
import openerp
|
||||
from openerp.osv import orm
|
||||
from openerp.http import request
|
||||
from openerp.addons.base import ir
|
||||
from openerp.http import request
|
||||
from openerp.osv import orm
|
||||
|
||||
from ..utils import slugify
|
||||
from website import get_current_website
|
||||
|
||||
class ir_http(orm.AbstractModel):
|
||||
_inherit = 'ir.http'
|
||||
|
@ -24,6 +26,33 @@ class ir_http(orm.AbstractModel):
|
|||
else:
|
||||
request.uid = request.session.uid
|
||||
|
||||
def _handle_403(self, exception):
|
||||
return self._render_error(403, {
|
||||
'error': exception.message
|
||||
})
|
||||
|
||||
def _handle_404(self, exception):
|
||||
return self._render_error(404)
|
||||
|
||||
def _handle_500(self, exception):
|
||||
# TODO: proper logging
|
||||
return self._render_error(500, {
|
||||
'exception': exception,
|
||||
'traceback': traceback.format_exc(),
|
||||
'qweb_template': getattr(exception, 'qweb_template', None),
|
||||
'qweb_node': getattr(exception, 'qweb_node', None),
|
||||
'qweb_eval': getattr(exception, 'qweb_eval', None),
|
||||
})
|
||||
|
||||
def _render_error(self, code, values=None):
|
||||
self._auth_method_public()
|
||||
if not hasattr(request, 'website'):
|
||||
request.website = get_current_website()
|
||||
request.website.preprocess_request(request)
|
||||
return werkzeug.wrappers.Response(
|
||||
request.website._render('website.%s' % code, values),
|
||||
status=code,
|
||||
content_type='text/html;charset=utf-8')
|
||||
|
||||
class ModelConverter(ir.ir_http.ModelConverter):
|
||||
def __init__(self, url_map, model=False):
|
||||
|
@ -38,25 +67,22 @@ class ModelConverter(ir.ir_http.ModelConverter):
|
|||
id, name = value
|
||||
return "%s-%d" % (slugify(name), id)
|
||||
|
||||
def generate(self, query=None):
|
||||
def generate(self, cr, uid, query=None, context=None):
|
||||
return request.registry[self.model].name_search(
|
||||
request.cr, request.uid,
|
||||
name=query or '',
|
||||
context=request.context)
|
||||
cr, uid, name=query or '', context=context)
|
||||
|
||||
class PageConverter(werkzeug.routing.PathConverter):
|
||||
""" Only point of this converter is to bundle pages enumeration logic
|
||||
|
||||
Sads got: no way to get the view's human-readable name even if one exists
|
||||
"""
|
||||
def generate(self, query=None):
|
||||
def generate(self, cr, uid, query=None, context=None):
|
||||
View = request.registry['ir.ui.view']
|
||||
views = View.search_read(
|
||||
request.cr, request.uid, [['page', '=', True]],
|
||||
fields=[], order='name', context=request.context)
|
||||
cr, uid, [['page', '=', True]],
|
||||
fields=[], order='name', context=context)
|
||||
xids = View.get_external_id(
|
||||
request.cr, request.uid, [view['id'] for view in views],
|
||||
context=request.context)
|
||||
cr, uid, [view['id'] for view in views], context=context)
|
||||
|
||||
for view in views:
|
||||
xid = xids[view['id']]
|
||||
|
|
|
@ -6,7 +6,6 @@ import logging
|
|||
import psycopg2
|
||||
import math
|
||||
import itertools
|
||||
import traceback
|
||||
import urllib
|
||||
import urlparse
|
||||
|
||||
|
@ -16,7 +15,6 @@ import werkzeug.exceptions
|
|||
import werkzeug.wrappers
|
||||
|
||||
import openerp
|
||||
from openerp.exceptions import AccessError, AccessDenied
|
||||
from openerp.osv import orm, osv, fields
|
||||
from openerp.tools.safe_eval import safe_eval
|
||||
|
||||
|
@ -41,29 +39,22 @@ def route(routes, *route_args, **route_kwargs):
|
|||
request.route_lang = kwargs.pop('lang_code', None)
|
||||
if not hasattr(request, 'website'):
|
||||
request.multilang = f.multilang
|
||||
# TODO: Select website, currently hard coded
|
||||
request.website = request.registry['website'].browse(
|
||||
request.cr, request.uid, 1, context=request.context)
|
||||
request.website = get_current_website()
|
||||
|
||||
if request.route_lang:
|
||||
lang_ok = [lg.code for lg in request.website.language_ids if lg.code == request.route_lang]
|
||||
if not lang_ok:
|
||||
return request.not_found()
|
||||
request.website.preprocess_request(request)
|
||||
try:
|
||||
return f(*args, **kwargs)
|
||||
except Exception, err:
|
||||
logger.exception("Website Rendering Error.")
|
||||
if request.context['is_public_user']:
|
||||
return request.website.render("website.401")
|
||||
else:
|
||||
return request.website.render("website.500", {
|
||||
'traceback': traceback.format_exc(),
|
||||
'controller': [f.__module__, "%s.%s" % (args[0].__class__.__name__, f.__name__)],
|
||||
})
|
||||
return f(*args, **kwargs)
|
||||
return wrap
|
||||
return decorator
|
||||
|
||||
def get_current_website():
|
||||
# TODO: Select website, currently hard coded
|
||||
return request.registry['website'].browse(request.cr, request.uid, 1, context=request.context)
|
||||
|
||||
|
||||
def url_for(path_or_uri, lang=None, keep_query=None):
|
||||
location = path_or_uri.strip()
|
||||
url = urlparse.urlparse(location)
|
||||
|
@ -124,29 +115,33 @@ class website(osv.osv):
|
|||
# completely arbitrary max_length
|
||||
idname = slugify(name, max_length=50)
|
||||
|
||||
cr.execute('SAVEPOINT pagenew')
|
||||
imd = self.pool.get('ir.model.data')
|
||||
view = self.pool.get('ir.ui.view')
|
||||
|
||||
module, tmp_page = template.split('.')
|
||||
view_model, view_id = imd.get_object_reference(cr, uid, module, tmp_page)
|
||||
|
||||
newview_id = view.copy(cr, uid, view_id, context=context)
|
||||
newview = view.browse(cr, uid, newview_id, context=context)
|
||||
newview.write({
|
||||
'arch': newview.arch.replace(template, "%s.%s" % (module, idname)),
|
||||
'name': name,
|
||||
'page': ispage,
|
||||
})
|
||||
imd.create(cr, uid, {
|
||||
'name': idname,
|
||||
'module': module,
|
||||
'model': 'ir.ui.view',
|
||||
'res_id': newview_id,
|
||||
'noupdate': True
|
||||
}, context=context)
|
||||
cr.execute('RELEASE SAVEPOINT pagenew')
|
||||
return "%s.%s" % (module, idname)
|
||||
cr.execute('SAVEPOINT new_page')
|
||||
try:
|
||||
newview_id = view.copy(cr, uid, view_id, context=context)
|
||||
newview = view.browse(cr, uid, newview_id, context=context)
|
||||
newview.write({
|
||||
'arch': newview.arch.replace(template, "%s.%s" % (module, idname)),
|
||||
'name': name,
|
||||
'page': ispage,
|
||||
})
|
||||
imd.create(cr, uid, {
|
||||
'name': idname,
|
||||
'module': module,
|
||||
'model': 'ir.ui.view',
|
||||
'res_id': newview_id,
|
||||
'noupdate': True
|
||||
}, context=context)
|
||||
cr.execute('RELEASE SAVEPOINT new_page')
|
||||
return "%s.%s" % (module, idname)
|
||||
except:
|
||||
cr.execute("ROLLBACK TO SAVEPOINT new_page")
|
||||
raise
|
||||
|
||||
def get_public_user(self, cr, uid, context=None):
|
||||
if not self.public_user:
|
||||
|
@ -159,6 +154,9 @@ class website(osv.osv):
|
|||
def redirect(url):
|
||||
return werkzeug.utils.redirect(url_for(url))
|
||||
request.redirect = redirect
|
||||
if not hasattr(request, 'multilang'):
|
||||
# TODO: try to get rid of this multilang attribute
|
||||
request.multilang = False
|
||||
|
||||
is_public_user = request.uid == self.get_public_user(cr, uid, context).id
|
||||
|
||||
|
@ -186,9 +184,17 @@ class website(osv.osv):
|
|||
'translatable': not is_public_user and not is_master_lang and request.multilang,
|
||||
})
|
||||
|
||||
def _render(self, cr, uid, ids, template, values=None, context=None):
|
||||
view = self.pool.get("ir.ui.view")
|
||||
def get_template(self, cr, uid, ids, template, context=None):
|
||||
IMD = self.pool.get("ir.model.data")
|
||||
try:
|
||||
module, xmlid = template.split('.', 1)
|
||||
view_ref = IMD.get_object_reference(cr, uid, module, xmlid)
|
||||
except ValueError: # catches both unpack errors and gor errors
|
||||
module, xmlid = 'website', template
|
||||
view_ref = IMD.get_object_reference(cr, uid, module, xmlid)
|
||||
return self.pool.get("ir.ui.view").browse(cr, uid, view_ref[1])
|
||||
|
||||
def _render(self, cr, uid, ids, template, values=None, context=None):
|
||||
user = self.pool.get("res.users")
|
||||
|
||||
if not context:
|
||||
|
@ -212,43 +218,12 @@ class website(osv.osv):
|
|||
inherit_branding=qweb_context.setdefault('editable', False),
|
||||
)
|
||||
|
||||
view_ref = None
|
||||
# check if xmlid of the template exists
|
||||
try:
|
||||
module, xmlid = template.split('.', 1)
|
||||
view_ref = IMD.get_object_reference(cr, uid, module, xmlid)
|
||||
except ValueError: # catches both unpack errors and gor errors
|
||||
module, xmlid = 'website', template
|
||||
try:
|
||||
view_ref = IMD.get_object_reference(cr, uid, module, xmlid)
|
||||
except ValueError:
|
||||
return self.error(cr, uid, 404, qweb_context, context=context)
|
||||
view = self.get_template(cr, uid, ids, template)
|
||||
|
||||
if 'main_object' not in qweb_context:
|
||||
try:
|
||||
main_object = self.pool[view_ref[0]].browse(cr, uid, view_ref[1])
|
||||
qweb_context['main_object'] = main_object
|
||||
except Exception:
|
||||
pass
|
||||
qweb_context['main_object'] = view
|
||||
|
||||
try:
|
||||
return view.render(
|
||||
cr, uid, "%s.%s" % (module, xmlid), qweb_context,
|
||||
engine='website.qweb', context=context)
|
||||
except (AccessError, AccessDenied), err:
|
||||
logger.error(err)
|
||||
qweb_context['error'] = err[1]
|
||||
logger.warn("Website Rendering Error.\n\n%s" % traceback.format_exc())
|
||||
return self.error(cr, uid, 401, qweb_context, context=context)
|
||||
except Exception, e:
|
||||
qweb_context['template'] = getattr(e, 'qweb_template', '')
|
||||
node = getattr(e, 'qweb_node', None)
|
||||
qweb_context['node'] = node and node.toxml()
|
||||
qweb_context['expr'] = getattr(e, 'qweb_eval', '')
|
||||
qweb_context['traceback'] = traceback.format_exc()
|
||||
logger.exception("Website Rendering Error.\n%(template)s\n%(expr)s\n%(node)s" % qweb_context)
|
||||
return self.error(cr, uid, 500 if qweb_context['editable'] else 404,
|
||||
qweb_context, context=context)
|
||||
return view.render(qweb_context, engine='website.qweb', context=context)
|
||||
|
||||
def render(self, cr, uid, ids, template, values=None, context=None):
|
||||
def callback(template, values, context):
|
||||
|
@ -257,13 +232,6 @@ class website(osv.osv):
|
|||
values = {}
|
||||
return LazyResponse(callback, template=template, values=values, context=context)
|
||||
|
||||
def error(self, cr, uid, code, qweb_context, context=None):
|
||||
View = request.registry['ir.ui.view']
|
||||
return werkzeug.wrappers.Response(
|
||||
View.render(cr, uid, 'website.%d' % code, qweb_context),
|
||||
status=code,
|
||||
content_type='text/html;charset=utf-8')
|
||||
|
||||
def pager(self, cr, uid, ids, url, total, page=1, step=30, scope=5, url_args=None, context=None):
|
||||
# Compute Pager
|
||||
page_count = int(math.ceil(float(total) / step))
|
||||
|
@ -386,6 +354,8 @@ class website(osv.osv):
|
|||
:rtype: list({name: str, url: str})
|
||||
"""
|
||||
router = request.httprequest.app.get_db_router(request.db)
|
||||
# Force enumeration to be performed as public user
|
||||
uid = self.get_public_user(cr, uid, context=context).id
|
||||
for rule in router.iter_rules():
|
||||
if not self.rule_is_enumerable(rule):
|
||||
continue
|
||||
|
@ -396,10 +366,10 @@ class website(osv.osv):
|
|||
# allow single converter as decided by fp, checked by
|
||||
# rule_is_enumerable
|
||||
[(name, converter)] = converters.items()
|
||||
# FIXME: perform generation as public user
|
||||
converter_values = converter.generate(
|
||||
request.cr, uid, query=query_string, context=context)
|
||||
generated = ({k: v} for k, v in itertools.izip(
|
||||
itertools.repeat(name),
|
||||
converter.generate(query=query_string)))
|
||||
itertools.repeat(name), converter_values))
|
||||
else:
|
||||
# force single iteration for literal urls
|
||||
generated = [{}]
|
||||
|
|
|
@ -103,7 +103,6 @@
|
|||
top: $el.height() / 2 + image_top - $btn.outerHeight() / 2,
|
||||
left: $el.width() / 2 + image_left - $btn.outerWidth() / 2,
|
||||
});
|
||||
$el.css('opacity', 0.75);
|
||||
}).on('mouseleave', 'img', function (e) {
|
||||
var $previous = $(previousSelection.$);
|
||||
var $button = $previous.next('button');
|
||||
|
@ -112,14 +111,11 @@
|
|||
$button.css('visibility', '');
|
||||
if (el === this) { return; }
|
||||
$button.remove();
|
||||
$previous.css('opacity', '');
|
||||
previousSelection = null;
|
||||
});
|
||||
editor.on('destroy', function (evt) {
|
||||
editor.on('destroy', function () {
|
||||
if (!previousSelection) { return; }
|
||||
$('.image-edit-button')
|
||||
.prev().css('opacity', '').end()
|
||||
.remove();
|
||||
$('.image-edit-button').remove();
|
||||
});
|
||||
|
||||
//noinspection JSValidateTypes
|
||||
|
@ -566,7 +562,9 @@
|
|||
window_title: "New Page",
|
||||
input: "Page Title",
|
||||
}).then(function (val) {
|
||||
document.location = '/pagenew/' + encodeURI(val);
|
||||
if (val) {
|
||||
document.location = '/pagenew/' + encodeURI(val);
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
|
@ -895,10 +893,9 @@
|
|||
start: function () {
|
||||
var self = this;
|
||||
this.$('#link-page').select2({
|
||||
minimumInputLength: 3,
|
||||
minimumInputLength: 1,
|
||||
placeholder: _t("New or existing page"),
|
||||
query: function (q) {
|
||||
// FIXME: out-of-order, abort
|
||||
self.fetch_pages(q.term).then(function (results) {
|
||||
var rs = _.map(results, function (r) {
|
||||
return { id: r.url, text: r.name, };
|
||||
|
@ -912,6 +909,8 @@
|
|||
more: false,
|
||||
results: rs
|
||||
});
|
||||
}, function () {
|
||||
q.callback({more: false, results: []});
|
||||
});
|
||||
},
|
||||
});
|
||||
|
@ -995,8 +994,8 @@
|
|||
limit: 9,
|
||||
context: website.get_context()
|
||||
},
|
||||
}).done(function () {
|
||||
// request completed successfully -> unstore it
|
||||
}).always(function () {
|
||||
// request completed -> unstore it
|
||||
self.req = null;
|
||||
});
|
||||
},
|
||||
|
|
|
@ -53,7 +53,7 @@
|
|||
|
||||
website.snippet.animationRegistry.carousel = website.snippet.Animation.extend({
|
||||
start: function () {
|
||||
this.$target.carousel({interval: false});
|
||||
this.$target.carousel({interval: 10000});
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -1125,7 +1125,8 @@
|
|||
id = _id + 1;
|
||||
}
|
||||
});
|
||||
this.$target.attr("id", "myCarousel" + id);
|
||||
this.id = "myCarousel" + id;
|
||||
this.$target.attr("id", this.id);
|
||||
this.$target.find(".carousel-control").attr("href", "#myCarousel" + id);
|
||||
this.$target.find("[data-target='#myCarousel']").attr("data-target", "#myCarousel" + id);
|
||||
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
(function () {
|
||||
'use strict';
|
||||
|
||||
var website = openerp.website;
|
||||
|
||||
website.EditorBar.include({
|
||||
start: function () {
|
||||
this.registerTour(new website.AppTour(this));
|
||||
return this._super();
|
||||
},
|
||||
});
|
||||
|
||||
website.AppTour = website.Tour.extend({
|
||||
id: 'app-tutorial',
|
||||
name: "Install a new application",
|
||||
init: function (editor) {
|
||||
var self = this;
|
||||
self.steps = [
|
||||
{
|
||||
stepId: 'welcome-install-app',
|
||||
orphan: true,
|
||||
backdrop: true,
|
||||
title: "Install an App",
|
||||
content: "You can intall some apps to manage your website content.",
|
||||
template: self.popover({ next: "Start Tutorial", end: "Skip It" }),
|
||||
},
|
||||
{
|
||||
stepId: 'customize',
|
||||
element: '#customize-menu-button',
|
||||
placement: 'left',
|
||||
title: "Install an app",
|
||||
content: "Add new apps by customizing your website.",
|
||||
triggers: function () {
|
||||
editor.on('rte:customize_menu_ready', editor, function () {
|
||||
self.movetoStep('install-app');
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
stepId: 'install-app',
|
||||
element: '#customize-menu li:last a',
|
||||
placement: 'left',
|
||||
orphan: true,
|
||||
title: "Install an app",
|
||||
content: "Click 'Install Apps' to select and install the application you would like to manage from your website.",
|
||||
},
|
||||
];
|
||||
return this._super();
|
||||
},
|
||||
});
|
||||
|
||||
}());
|
|
@ -32,9 +32,7 @@
|
|||
title: "Edit this page",
|
||||
content: "Every page of your website can be modified through the <i>Edit</i> button.",
|
||||
triggers: function () {
|
||||
editor.on('tour:editor_bar_loaded', editor, function () {
|
||||
self.movetoStep('add-block');
|
||||
});
|
||||
editor.on('tour:editor_bar_loaded', self, self.moveToNextStep);
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -44,8 +42,8 @@
|
|||
title: "Insert building blocks",
|
||||
content: "To add content in a page, you can insert building blocks.",
|
||||
triggers: function () {
|
||||
$('button[data-action=snippet]').click(function () {
|
||||
self.movetoStep('drag-banner');
|
||||
$('button[data-action=snippet]').one('click', function () {
|
||||
self.moveToNextStep();
|
||||
});
|
||||
}
|
||||
},
|
||||
|
@ -56,7 +54,7 @@
|
|||
title: "Drag & Drop a Banner",
|
||||
content: "Drag the Banner block and drop it in your page.",
|
||||
triggers: function () {
|
||||
self.onSnippetDraggedMoveTo('edit-title');
|
||||
self.onSnippetDraggedAdvance('carousel');
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -80,41 +78,98 @@
|
|||
title: "Customize the banner",
|
||||
content: "You can customize characteristic of any blocks through the Customize menu. For instance, change the background of the banner.",
|
||||
template: self.popover({ next: "Continue" }),
|
||||
},
|
||||
{
|
||||
stepId: 'add-three-cols',
|
||||
element: 'button[data-action=snippet]',
|
||||
placement: 'bottom',
|
||||
title: "Add Another Block",
|
||||
content: "Let's add another building block to your page.",
|
||||
triggers: function () {
|
||||
$('.dropdown-menu [name=carousel-background]').click(function () {
|
||||
self.movetoStep('save-changes');
|
||||
$('button[data-action=snippet]').one('click', function () {
|
||||
self.moveToNextStep();
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
stepId: 'drag-three-columns',
|
||||
element: '#website-top-navbar [data-snippet-id=three-columns].ui-draggable',
|
||||
placement: 'bottom',
|
||||
title: "Drag & Drop a Block",
|
||||
content: "Drag the <em>'3 Columns'</em> block and drop it below the banner.",
|
||||
triggers: function () {
|
||||
self.onSnippetDraggedAdvance('three-columns');
|
||||
},
|
||||
},
|
||||
{
|
||||
stepId: 'activate-text-block-title',
|
||||
element: '#wrap [data-snippet-id=three-columns] .text-center[data-snippet-id=colmd]',
|
||||
placement: 'top',
|
||||
title: "Edit an Area",
|
||||
content: "Select any area of the page to modify it. Click on this subtitle.",
|
||||
triggers: function () {
|
||||
$('#wrap [data-snippet-id=three-columns] .text-center[data-snippet-id=colmd]').one('click', function () {
|
||||
self.moveToNextStep();
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
stepId: 'remove-text-block-title',
|
||||
element: '.oe_snippet_remove:last',
|
||||
placement: 'top',
|
||||
reflex: true,
|
||||
title: "Delete the Title",
|
||||
content: "From this toolbar you can move, duplicate or delete the selected zone. Click on the cross to delete the title.",
|
||||
},
|
||||
{
|
||||
stepId: 'save-changes',
|
||||
element: 'button[data-action=save]',
|
||||
placement: 'right',
|
||||
reflex: true,
|
||||
title: "Save your modifications",
|
||||
content: "Once you click on save, your website page is updated.",
|
||||
content: "Publish your page by clicking on the <em>'Save'</em> button.",
|
||||
},
|
||||
{
|
||||
stepId: 'part-2',
|
||||
orphan: true,
|
||||
title: "Congratulation!",
|
||||
content: "Your homepage have been updated. Now, we suggest you insert another snippet to overview possible customization.",
|
||||
content: "Your homepage has been updated.",
|
||||
template: self.popover({ next: "Continue" }),
|
||||
},
|
||||
{
|
||||
stepId: 'show-mobile',
|
||||
element: 'a[data-action=show-mobile-preview]',
|
||||
placement: 'bottom',
|
||||
title: "Test Your Mobile Version",
|
||||
content: "Let's check how your homepage looks like on mobile devices.",
|
||||
triggers: function () {
|
||||
$(document).one('shown.bs.modal', function () {
|
||||
self.moveToNextStep();
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
stepId: 'show-mobile-close',
|
||||
element: 'button[data-dismiss=modal]',
|
||||
placement: 'right',
|
||||
reflex: true,
|
||||
title: "Close Mobile Preview",
|
||||
content: "Scroll in the mobile preview to test the rendering. Once it's ok, close this dialog.",
|
||||
},
|
||||
{
|
||||
stepId: 'show-tutorials',
|
||||
element: '#help-menu-button',
|
||||
placement: 'left',
|
||||
reflex: true,
|
||||
title: "Help is always available",
|
||||
content: "You can always click here if you want more helps or continue to build and get more tips about your website contents like page menu, ...",
|
||||
template: self.popover({ end: "Close" }),
|
||||
title: "More Tutorials",
|
||||
content: "Get more tutorials through this <em>'Help'</em> menu or click on the left <em>'Edit'</em> button to continue modifying this page.",
|
||||
template: self.popover({ end: "Close Tutorial" }),
|
||||
}
|
||||
];
|
||||
return this._super();
|
||||
},
|
||||
resume: function () {
|
||||
return (this.isCurrentStep('part-2') || this.isCurrentStep('show-tutorials')) && !this.tour.ended();
|
||||
return this.isCurrentStep('part-2') && this._super();
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -48,13 +48,21 @@
|
|||
isCurrentStep: function (stepId) {
|
||||
return this.currentStepIndex() === this.indexOfStep(stepId);
|
||||
},
|
||||
movetoStep: function (stepId) {
|
||||
$('.popover.tour').remove();
|
||||
var index = this.indexOfStep(stepId);
|
||||
if (index > -1) {
|
||||
this.tour.goto(index);
|
||||
moveToStep: function (step) {
|
||||
var index = _.isNumber(step) ? step : this.indexOfStep(step);
|
||||
if (index >= this.steps.length) {
|
||||
this.stop();
|
||||
} else if (index >= 0) {
|
||||
var self = this;
|
||||
setTimeout(function () {
|
||||
$('.popover.tour').remove();
|
||||
self.tour.goto(index);
|
||||
}, 0);
|
||||
}
|
||||
},
|
||||
moveToNextStep: function () {
|
||||
this.moveToStep(this.currentStepIndex() + 1);
|
||||
},
|
||||
stop: function () {
|
||||
this.tour.end();
|
||||
},
|
||||
|
@ -65,9 +73,12 @@
|
|||
window.location.replace(newUrl);
|
||||
}
|
||||
},
|
||||
ended: function () {
|
||||
return this.tourStorage.getItem(this.id+'_end') === "yes";
|
||||
},
|
||||
resume: function () {
|
||||
// Override if necessary
|
||||
return this.currentStepIndex() === 0;
|
||||
return !this.ended();
|
||||
},
|
||||
trigger: function (url) {
|
||||
// Override if necessary
|
||||
|
@ -82,25 +93,21 @@
|
|||
popover: function (options) {
|
||||
return openerp.qweb.render('website.tour_popover', options);
|
||||
},
|
||||
onSnippetDraggedMoveTo: function (stepId) {
|
||||
onSnippetDraggedAdvance: function (snippetId, stepId) {
|
||||
var self = this;
|
||||
var $body = $(document.body);
|
||||
function beginDrag () {
|
||||
$('.popover.tour').remove();
|
||||
function goToNextStep () {
|
||||
$('#snippets').toggle();
|
||||
function advance () {
|
||||
if (stepId) {
|
||||
self.movetoStep(stepId);
|
||||
self.moveToStep(stepId);
|
||||
} else {
|
||||
self.stop();
|
||||
self.moveToNextStep()
|
||||
}
|
||||
$body.off('mouseup', goToNextStep);
|
||||
}
|
||||
$body.off('mousedown', beginDrag);
|
||||
$body.on('mouseup', goToNextStep);
|
||||
$(document.body).one('mouseup', advance);
|
||||
}
|
||||
$body.on('mousedown', beginDrag);
|
||||
}
|
||||
$('#website-top-navbar [data-snippet-id='+snippetId+'].ui-draggable').one('mousedown', beginDrag);
|
||||
},
|
||||
});
|
||||
|
||||
website.UrlParser = openerp.Class.extend({
|
||||
|
|
|
@ -1,100 +0,0 @@
|
|||
(function () {
|
||||
'use strict';
|
||||
|
||||
var website = openerp.website;
|
||||
|
||||
website.EditorBar.include({
|
||||
start: function () {
|
||||
this.registerTour(new website.PageTour(this));
|
||||
return this._super();
|
||||
},
|
||||
});
|
||||
|
||||
website.PageTour = website.Tour.extend({
|
||||
id: 'page-tutorial',
|
||||
name: "Add a page",
|
||||
init: function (editor) {
|
||||
var self = this;
|
||||
self.steps = [
|
||||
{
|
||||
stepId: 'welcome-menu-editor',
|
||||
orphan: true,
|
||||
backdrop: true,
|
||||
title: "Menu editor",
|
||||
content: "We will show how to edit your website's menu.",
|
||||
template: self.popover({ next: "Start Tutorial", end: "Skip It" }),
|
||||
},
|
||||
{
|
||||
stepId: 'content-menu',
|
||||
element: '#content-menu-button',
|
||||
placement: 'left',
|
||||
reflex: true,
|
||||
title: "Edit the content",
|
||||
content: "Click here to edit the menu.",
|
||||
},
|
||||
{
|
||||
stepId: 'edit-entry',
|
||||
element: 'a[data-action=edit-structure]',
|
||||
placement: 'left',
|
||||
title: "Edit menu",
|
||||
content: "Click here to create a new menu entry and manage options.",
|
||||
triggers: function () {
|
||||
$(document).one('shown.bs.modal', function () {
|
||||
self.movetoStep('add-menu-entry');
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
stepId: 'add-menu-entry',
|
||||
element: 'a.js_add_menu',
|
||||
placement: 'left',
|
||||
title: "Add menu entry",
|
||||
content: "Click here to create a new menu entry.",
|
||||
triggers: function () {
|
||||
$(document).one('shown.bs.modal', function () {
|
||||
self.movetoStep('enter-entry-name');
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
stepId: 'enter-entry-name',
|
||||
element: '#link-text',
|
||||
placement: 'left',
|
||||
title: "Choose a label",
|
||||
content: "This label will appear in the top menu and will be visible by all your audience.\nGive a meaningful name to help your visitors. For instance, 'Photos Gallery'.",
|
||||
template: self.popover({ next: "Continue" }),
|
||||
},
|
||||
{
|
||||
stepId: 'enter-page-name',
|
||||
element: '.modal .select2-container',
|
||||
placement: 'left',
|
||||
title: "Link your menu to a 'gallery' page",
|
||||
content: "This page does not exist. Create it by filling the name here. For instance, 'gallery'.",
|
||||
template: self.popover({ next: "Continue" }),
|
||||
},
|
||||
{
|
||||
stepId: 'save-page',
|
||||
element: '.modal-footer:last button.btn-primary',
|
||||
placement: 'right',
|
||||
title: "Save the page",
|
||||
content: "Save your new page.",
|
||||
triggers: function () {
|
||||
$(document).one('hidden.bs.modal', function () {
|
||||
self.movetoStep('save-menu');
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
stepId: 'save-menu',
|
||||
element: '.modal-footer button.btn-primary',
|
||||
placement: 'right',
|
||||
reflex: true,
|
||||
title: "Save the menu",
|
||||
content: "Save the menu to edit the Gallery content directly from the interface.",
|
||||
},
|
||||
];
|
||||
return this._super();
|
||||
},
|
||||
});
|
||||
|
||||
}());
|
|
@ -57,16 +57,7 @@
|
|||
<h3 class="modal-title">Mobile preview</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<iframe id="mobile-viewport" class="oe_mobile_viewport" src="?mobile-preview=true"></iframe>
|
||||
<script>
|
||||
var $o = $('<div style="width:50px; overflow:scroll;"><div style="width:100%;"></div></div>');
|
||||
$o.appendTo("body");
|
||||
var scrollSize = 50 - $o.find("div").innerWidth();
|
||||
$o.remove();
|
||||
|
||||
$(".modal-body").css({'overflow': 'hidden', 'margin-right': '5px'});
|
||||
$(".oe_mobile_viewport").css({'width': (320 + scrollSize + 5) + 'px'});
|
||||
</script>
|
||||
<iframe id="mobile-viewport" class="oe_mobile_viewport" src="?mobile-preview=true" style="width: 326px"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -6,10 +6,11 @@ class URLCase(unittest2.TestCase):
|
|||
URLCase moved out of test_requests, otherwise discovery attempts to
|
||||
instantiate and run it
|
||||
"""
|
||||
def __init__(self, user, url, result):
|
||||
def __init__(self, user, url, source, result):
|
||||
super(URLCase, self).__init__()
|
||||
self.user = user
|
||||
self.url = url
|
||||
self.source = source
|
||||
self.result = result
|
||||
|
||||
@property
|
||||
|
@ -17,6 +18,8 @@ class URLCase(unittest2.TestCase):
|
|||
return self.user or "Anonymous Coward"
|
||||
|
||||
def __str__(self):
|
||||
if self.source:
|
||||
return "%s (from %s, as %s)" % (self.url, self.source, self.username)
|
||||
return "%s (as %s)" % (self.url, self.username)
|
||||
|
||||
__repr__ = __str__
|
||||
|
|
|
@ -80,13 +80,13 @@ class CrawlSuite(unittest2.TestSuite):
|
|||
assert auth.getcode() < 400, "Auth failure %d" % auth.getcode()
|
||||
|
||||
def _wrapped_run(self, result, debug=False):
|
||||
paths = collections.deque(['/'])
|
||||
paths = [URL('/'), URL('/sitemap')]
|
||||
seen = set(paths)
|
||||
|
||||
while paths:
|
||||
url = paths.popleft()
|
||||
r = self._request(url)
|
||||
cases.URLCase(self.user, url, r).run(result)
|
||||
url = paths.pop(0)
|
||||
r = self._request(url.url)
|
||||
url.to_case(self.user, r).run(result)
|
||||
|
||||
if r.info().gettype() != 'text/html':
|
||||
continue
|
||||
|
@ -110,7 +110,15 @@ class CrawlSuite(unittest2.TestSuite):
|
|||
(parts.scheme and parts.scheme not in ('http', 'https')):
|
||||
continue
|
||||
|
||||
paths.append(href)
|
||||
paths.append(URL(href, url.url))
|
||||
|
||||
class URL(object):
|
||||
def __init__(self, url, source=None):
|
||||
self.url = url
|
||||
self.source = source
|
||||
|
||||
def to_case(self, user, result):
|
||||
return cases.URLCase(user, self.url, self.source, result)
|
||||
|
||||
def load_tests(loader, base, _):
|
||||
base.addTest(CrawlSuite())
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
</template>
|
||||
|
||||
<template id="layout" name="Main layout"><!DOCTYPE html>
|
||||
<html t-att-lang="lang.replace('_', '-')"
|
||||
<html t-att-lang="lang and lang.replace('_', '-')"
|
||||
t-att-data-website-id="website.id if editable else None"
|
||||
t-att-data-editable="'1' if editable else None"
|
||||
t-att-data-translatable="'1' if translatable else None"
|
||||
|
@ -66,7 +66,6 @@
|
|||
<script type="text/javascript" src="/web/static/lib/underscore/underscore.js"></script>
|
||||
<script type="text/javascript" src="/web/static/lib/underscore.string/lib/underscore.string.js"></script>
|
||||
<script type="text/javascript" src="/web/static/lib/jquery/jquery.js"></script>
|
||||
<script type="text/javascript" src="/web/static/lib/jquery.form/jquery.form.js"></script>
|
||||
<script type="text/javascript" src="/website/static/lib/bootstrap/js/bootstrap.js"></script>
|
||||
<script type="text/javascript">
|
||||
// Bootstrap and jQuery UI conflicts
|
||||
|
@ -103,8 +102,6 @@
|
|||
<script type="text/javascript" src="/website/static/src/js/website.seo.js"></script>
|
||||
<script type="text/javascript" src="/website/static/src/js/website.tour.js"></script>
|
||||
<script type="text/javascript" src="/website/static/src/js/website.tour.banner.js"></script>
|
||||
<script type="text/javascript" src="/website/static/src/js/website.tour.page.js"></script>
|
||||
<script type="text/javascript" src="/website/static/src/js/website.tour.app.js"></script>
|
||||
<script t-if="not translatable" type="text/javascript" src="/website/static/src/js/website.snippets.editor.js"></script>
|
||||
<script t-if="not translatable" type="text/javascript" src="/website/static/src/js/website.ace.js"></script>
|
||||
<script t-if="translatable" type="text/javascript" src="/website/static/src/js/website.translator.js"></script>
|
||||
|
@ -216,7 +213,7 @@
|
|||
<a class="label label-danger" href="https://openerp.com/apps/website">OpenERP</a>
|
||||
</div>
|
||||
<div class="pull-left text-muted">
|
||||
Copyright &copy; <span t-field="res_company.name">Company name</span> - <a href="/sitemap">Sitemap</a>
|
||||
Copyright &copy; <span t-field="res_company.name">Company name</span>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
@ -366,54 +363,40 @@
|
|||
</t>
|
||||
</template>
|
||||
|
||||
<template id="403">
|
||||
<t t-call="website.layout">
|
||||
<div id="wrap">
|
||||
<div class="oe_structure oe_empty">
|
||||
<div class="container">
|
||||
<h1 class="mt32">403: Forbidden!</h1>
|
||||
<p>
|
||||
Access Denied.
|
||||
</p>
|
||||
<p>Maybe you were looking for one of these popular pages ?</p>
|
||||
<ul>
|
||||
<li><a href="/">Homepage</a></li>
|
||||
<li><a href="/page/website.contactus/">Contact Us</a></li>
|
||||
</ul>
|
||||
<template id="page_404">
|
||||
<t t-call="website.404">
|
||||
<div class="container">
|
||||
<div class="well mt32">
|
||||
<p>This page does not exists, but you can create it as you are administrator of this site.</p>
|
||||
<a class="btn btn-primary" t-attf-href="/pagenew/#{ path }">Create Page</a>
|
||||
<span class="text-muted">or</span> <a href="/sitemap">Search a Page</a>
|
||||
</div>
|
||||
<div class="text-center text-muted">Edit the content bellow this line to adapt the default "page not found" page.</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr/>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<template id="404">
|
||||
<t t-call="website.layout">
|
||||
<div id="wrap">
|
||||
<div class="container" t-if="editable and path">
|
||||
<div class="well mt32">
|
||||
<p>This page does not exists, but you can create it as you are administrator of this site.</p>
|
||||
<a class="btn btn-primary" t-att-href="'/pagenew/'+path">Create Page</a>
|
||||
<span class="text-muted">or</span> <a href="/sitemap">Search a Page</a>
|
||||
</div>
|
||||
<div class="text-center text-muted">Edit the content bellow this line to adapt the default "page not found" page.</div>
|
||||
</div>
|
||||
<hr />
|
||||
<div class="oe_structure oe_empty">
|
||||
<div class="container">
|
||||
<h1 class="mt32">404: Page not found!</h1>
|
||||
<p>
|
||||
The page you were looking for could not be found; it is possible you have
|
||||
typed the address incorrectly, but it has most probably been removed due
|
||||
to the recent website reorganisation.
|
||||
</p>
|
||||
<p>Maybe you were looking for one of these popular pages ?</p>
|
||||
<ul>
|
||||
<li><a href="/">Homepage</a></li>
|
||||
<li><a href="/page/website.contactus/">Contact Us</a></li>
|
||||
</ul>
|
||||
<div id="wrap">
|
||||
<t t-raw="0"/>
|
||||
<div class="oe_structure oe_empty">
|
||||
<div class="container">
|
||||
<h1 class="mt32">404: Page not found!</h1>
|
||||
<p>
|
||||
The page you were looking for could not be found; it is possible you have
|
||||
typed the address incorrectly, but it has most probably been removed due
|
||||
to the recent website reorganisation.
|
||||
</p>
|
||||
<p>Maybe you were looking for one of these popular pages ?</p>
|
||||
<ul>
|
||||
<li><a href="/">Homepage</a></li>
|
||||
<li><a href="/page/website.contactus/">Contact Us</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
|
@ -423,22 +406,67 @@
|
|||
<div class="oe_structure">
|
||||
<h1 class="container mt32">500: Internal Server Error!</h1>
|
||||
</div>
|
||||
<t t-if="editable">
|
||||
<h3 t-if="template">Exception in template: <span id="exception_template" t-esc="template"/></h3>
|
||||
<h3 t-if="controller">Exception in controller: <span id="exception_controller" t-esc="controller[1]"/> <small>(Module: <span id="exception_module" t-esc="controller[0]"/>)</small> </h3>
|
||||
<h4 t-if="expr">Expression: <t t-esc="expr"/></h4>
|
||||
<t t-if="node"><pre id="exception_node" t-esc="node"/></t>
|
||||
<pre id="exception_traceback" t-esc="traceback"/>
|
||||
</t>
|
||||
|
||||
<div class="container panel-group" id="debug_infos" t-if="editable">
|
||||
<div class="panel panel-default" t-if="exception">
|
||||
<div class="panel-heading">
|
||||
<h4 class="panel-title">
|
||||
<a data-toggle="collapse" data-parent="#debug_infos" href="#error_main">
|
||||
Error
|
||||
</a>
|
||||
</h4>
|
||||
</div>
|
||||
<div id="error_main" class="panel-collapse collapse in">
|
||||
<div class="panel-body">
|
||||
<p t-if="website_controller">The following error was raised in the website controller <code t-esc="website_controller"/></p>
|
||||
<p><strong>Error message:</strong> <t t-esc="exception.message"/></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default" t-if="qweb_template">
|
||||
<div class="panel-heading">
|
||||
<h4 class="panel-title">
|
||||
<a data-toggle="collapse" data-parent="#adebug_infos" href="#error_qweb">
|
||||
QWeb
|
||||
</a>
|
||||
</h4>
|
||||
</div>
|
||||
<div id="error_qweb" class="panel-collapse collapse">
|
||||
<div class="panel-body">
|
||||
<p>
|
||||
The error occured while rendering the template <code t-esc="qweb_template"/>
|
||||
<t t-if="qweb_eval">and evaluating the following expression: <code t-esc="qweb_eval"/></t>
|
||||
</p>
|
||||
<t t-if="qweb_node">
|
||||
<pre id="exception_node" t-esc="qweb_node.toxml()"/>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default" t-if="traceback">
|
||||
<div class="panel-heading">
|
||||
<h4 class="panel-title">
|
||||
<a data-toggle="collapse" data-parent="#adebug_infos" href="#error_traceback">
|
||||
Traceback
|
||||
</a>
|
||||
</h4>
|
||||
</div>
|
||||
<div id="error_traceback" class="panel-collapse collapse">
|
||||
<div class="panel-body">
|
||||
<pre id="exception_traceback" t-esc="traceback"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<template id="401">
|
||||
<template id="403">
|
||||
<t t-call="website.layout">
|
||||
<div id="wrap">
|
||||
<div class="container">
|
||||
<h1 class="mt32">401: Unauthorized Access!</h1>
|
||||
<h1 class="mt32">403: Forbidden</h1>
|
||||
<p>
|
||||
The page you were looking for could not be
|
||||
authorized.
|
||||
|
|
|
@ -215,7 +215,7 @@ class WebsiteBlog(http.Controller):
|
|||
return request.website.render("website_blog.blog_post_complete", values)
|
||||
|
||||
@website.route(['/blog/<int:blog_post_id>/comment'], type='http', auth="public")
|
||||
def blog_post_comment(self, blog_post_id=None, **post):
|
||||
def blog_post_comment(self, blog_post_id, **post):
|
||||
cr, uid, context = request.cr, request.uid, request.context
|
||||
if post.get('comment'):
|
||||
request.registry['blog.post'].message_post(
|
||||
|
@ -226,8 +226,8 @@ class WebsiteBlog(http.Controller):
|
|||
context=dict(context, mail_create_nosubcribe=True))
|
||||
return werkzeug.utils.redirect(request.httprequest.referrer + "#comments")
|
||||
|
||||
@website.route(['/blog/new'], type='http', auth="public", multilang=True)
|
||||
def blog_post_create(self, category_id=None, **post):
|
||||
@website.route('/blog/new', type='http', auth="public", multilang=True)
|
||||
def blog_post_create(self, category_id, **post):
|
||||
cr, uid, context = request.cr, request.uid, request.context
|
||||
create_context = dict(context, mail_create_nosubscribe=True)
|
||||
new_blog_post_id = request.registry['blog.post'].create(
|
||||
|
@ -239,8 +239,8 @@ class WebsiteBlog(http.Controller):
|
|||
}, context=create_context)
|
||||
return werkzeug.utils.redirect("/blog/%s/?enable_editor=1" % new_blog_post_id)
|
||||
|
||||
@website.route(['/blog/duplicate'], type='http', auth="public")
|
||||
def blog_post_copy(self, blog_post_id=None, **post):
|
||||
@website.route('/blog/duplicate', type='http', auth="public")
|
||||
def blog_post_copy(self, blog_post_id, **post):
|
||||
""" Duplicate a blog.
|
||||
|
||||
:param blog_post_id: id of the blog post currently browsed.
|
||||
|
|
|
@ -16,34 +16,40 @@
|
|||
init: function (editor) {
|
||||
var self = this;
|
||||
self.steps = [
|
||||
{
|
||||
{
|
||||
stepId: 'welcome-blog',
|
||||
orphan: true,
|
||||
backdrop: true,
|
||||
title: "Blog",
|
||||
content: "We will show how to create a new blog post.",
|
||||
template: self.popover({ next: "Start Tutorial", end: "Skip It" }),
|
||||
title: "New Blog Post",
|
||||
content: "Let's go through the first steps to write beautiful blog posts.",
|
||||
template: self.popover({ next: "Start Tutorial", end: "Skip" }),
|
||||
},
|
||||
{
|
||||
stepId: 'content-menu',
|
||||
element: '#content-menu-button',
|
||||
placement: 'left',
|
||||
reflex: true,
|
||||
title: "Edit the content",
|
||||
content: "Click here to add content to your site.",
|
||||
title: "Add Content",
|
||||
content: "Create new pages, blogs, menu items and products through the <em>'Content'</em> menu.",
|
||||
},
|
||||
{
|
||||
stepId: 'new-post-entry',
|
||||
element: 'a[data-action=new_blog_post]',
|
||||
placement: 'left',
|
||||
title: "New blog post",
|
||||
content: "Click here to create a blog post.",
|
||||
title: "New Blog Post",
|
||||
content: "Select this entry to create a new blog post.",
|
||||
triggers: function () {
|
||||
$(document).one('shown.bs.modal', function () {
|
||||
$('.modal button.btn-primary').click(function () {
|
||||
self.movetoStep('post-page');
|
||||
var $doc = $(document);
|
||||
function stopNewBlog () {
|
||||
self.stop();
|
||||
}
|
||||
$doc.on('hide.bs.modal', stopNewBlog);
|
||||
$doc.one('shown.bs.modal', function () {
|
||||
$('.modal button.btn-primary').one('click', function () {
|
||||
$doc.off('hide.bs.modal', stopNewBlog);
|
||||
self.moveToStep('post-page');
|
||||
});
|
||||
self.movetoStep('choose-category');
|
||||
self.moveToNextStep();
|
||||
});
|
||||
},
|
||||
},
|
||||
|
@ -51,72 +57,72 @@
|
|||
stepId: 'choose-category',
|
||||
element: '.modal select',
|
||||
placement: 'right',
|
||||
title: "Choose the post category",
|
||||
content: "Select the 'News' category and click 'Continue'.",
|
||||
title: "Which Blog?",
|
||||
content: "Blog posts are organized in multiple categories (news, job offers, events, etc). Select <em>News</em> and click <em>Continue</em>.",
|
||||
triggers: function () {
|
||||
$('.modal select').change(function () {
|
||||
function newsSelected () {
|
||||
var $this = $(this);
|
||||
var selected = $this.find("[value="+$this.val()+"]").text();
|
||||
if (selected.toLowerCase() === 'news') {
|
||||
self.movetoStep('continue-category');
|
||||
if ($this.find('[value='+$this.val()+']').text().toLowerCase() === 'news') {
|
||||
self.moveToNextStep();
|
||||
$('.modal select').off('change', newsSelected);
|
||||
}
|
||||
});
|
||||
}
|
||||
$('.modal select').on('change', newsSelected);
|
||||
},
|
||||
},
|
||||
{
|
||||
stepId: 'continue-category',
|
||||
element: '.modal button.btn-primary',
|
||||
placement: 'right',
|
||||
title: "Choose the post category",
|
||||
content: "Click 'Continue' to create the post.",
|
||||
title: "Create Blog Post",
|
||||
content: "Click <em>Continue</em> to create the blog post.",
|
||||
},
|
||||
{
|
||||
stepId: 'post-page',
|
||||
orphan: true,
|
||||
backdrop: true,
|
||||
title: "New blog post created",
|
||||
content: "You just created a new blog post. We are now going to edit it.",
|
||||
template: self.popover({ next: "OK" }),
|
||||
title: "Blog Post Created",
|
||||
content: "This is your new blog post. We will edit your pages inline. What You See Is What You Get. No need for a complex backend.",
|
||||
template: self.popover({ next: "Continue" }),
|
||||
},
|
||||
{
|
||||
stepId: 'post-title',
|
||||
element: 'h1[data-oe-expression="blog_post.name"]',
|
||||
placement: 'top',
|
||||
title: "Pick a title",
|
||||
content: "Choose a catchy title for your blog post.",
|
||||
title: "Pick a Title",
|
||||
content: "Click on this area and set a catchy title.",
|
||||
template: self.popover({ next: "OK" }),
|
||||
},
|
||||
{
|
||||
stepId: 'add-image-block',
|
||||
stepId: 'add-image-text',
|
||||
element: 'button[data-action=snippet]',
|
||||
placement: 'bottom',
|
||||
title: "Layout your blog post",
|
||||
content: "Insert blocks like text-image to layout the body of your blog post.",
|
||||
title: "Layout Your Blog Post",
|
||||
content: "Use well designed building blocks to structure the content of your blog.",
|
||||
triggers: function () {
|
||||
$('button[data-action=snippet]').click(function () {
|
||||
self.movetoStep('drag-image-text');
|
||||
$('button[data-action=snippet]').one('click', function () {
|
||||
self.moveToNextStep();
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
stepId: 'drag-image-text',
|
||||
element: '#website-top-navbar [data-snippet-id=image-text].ui-draggable',
|
||||
placement: 'bottom',
|
||||
title: "Drag & Drop a block",
|
||||
content: "Drag the 'Image Text' block and drop it in your page.",
|
||||
title: "Drag & Drop a Block",
|
||||
content: "Drag the <em>'Image-Text'</em> block and drop it in your page.",
|
||||
triggers: function () {
|
||||
self.onSnippetDraggedMoveTo('add-text-block');
|
||||
self.onSnippetDraggedAdvance('image-text');
|
||||
},
|
||||
},
|
||||
{
|
||||
stepId: 'add-text-block',
|
||||
element: 'button[data-action=snippet]',
|
||||
placement: 'bottom',
|
||||
title: "Add another block",
|
||||
content: "Let's add another blog to your post.",
|
||||
title: "Add Another Block",
|
||||
content: "Let's add another block to your post.",
|
||||
triggers: function () {
|
||||
$('button[data-action=snippet]').click(function () {
|
||||
self.movetoStep('drag-text-block');
|
||||
$('button[data-action=snippet]').on('click', function () {
|
||||
self.moveToNextStep();
|
||||
});
|
||||
}
|
||||
},
|
||||
|
@ -125,20 +131,20 @@
|
|||
element: '#website-top-navbar [data-snippet-id=text-block].ui-draggable',
|
||||
placement: 'bottom',
|
||||
title: "Drag & Drop a block",
|
||||
content: "Drag the 'Text Block' block and drop it below the image block.",
|
||||
content: "Drag the <em>'Text Block'</em> block and drop it below the image block.",
|
||||
triggers: function () {
|
||||
self.onSnippetDraggedMoveTo('activate-text-block-title');
|
||||
self.onSnippetDraggedAdvance('text-block');
|
||||
},
|
||||
},
|
||||
{
|
||||
stepId: 'activate-text-block-title',
|
||||
element: '#wrap [data-snippet-id=text-block] .text-center[data-snippet-id=colmd]',
|
||||
placement: 'top',
|
||||
title: "Activate on the title",
|
||||
content: "Click on the title to activate it.",
|
||||
title: "Edit an Area",
|
||||
content: "Select any area of the page to modify it. Click on this subtitle.",
|
||||
triggers: function () {
|
||||
$('#wrap [data-snippet-id=text-block] .text-center[data-snippet-id=colmd]').click(function () {
|
||||
self.movetoStep('remove-text-block-title');
|
||||
$('#wrap [data-snippet-id=text-block] .text-center[data-snippet-id=colmd]').one('click', function () {
|
||||
self.moveToNextStep();
|
||||
});
|
||||
},
|
||||
},
|
||||
|
@ -147,33 +153,41 @@
|
|||
element: '.oe_snippet_remove:last',
|
||||
placement: 'top',
|
||||
reflex: true,
|
||||
title: "Delete the title",
|
||||
content: "Click on the cross to delete the title.",
|
||||
},
|
||||
{
|
||||
stepId: 'publish-post',
|
||||
element: 'button.js_publish_btn',
|
||||
placement: 'right',
|
||||
reflex: true,
|
||||
title: "Publish your blog post",
|
||||
content: "Click to publish your blog post.",
|
||||
title: "Delete the Title",
|
||||
content: "From this toolbar you can move, duplicate or delete the selected zone. Click on the cross to delete the title.",
|
||||
},
|
||||
{
|
||||
stepId: 'save-changes',
|
||||
element: 'button[data-action=save]',
|
||||
placement: 'right',
|
||||
reflex: true,
|
||||
title: "Save your modifications",
|
||||
content: "Once you click on save, your post is updated.",
|
||||
title: "Save Your Blog",
|
||||
content: "Click the <em>Save</em> button to record changes on the page.",
|
||||
},
|
||||
{
|
||||
stepId: 'publish-post',
|
||||
element: 'button.js_publish_btn',
|
||||
placement: 'right',
|
||||
reflex: true,
|
||||
title: "Publish Your Post",
|
||||
content: "Your blog post is not yet published. You can update this draft version and publish it once you are ready.",
|
||||
},
|
||||
{
|
||||
stepId: 'end-tutorial',
|
||||
orphan: true,
|
||||
backdrop: true,
|
||||
title: "Thanks!",
|
||||
content: "This tutorial is finished. To discover more features, improve the content of this page and try the <em>Promote</em> button in the top right menu.",
|
||||
template: self.popover({ end: "Close Tutorial" }),
|
||||
},
|
||||
];
|
||||
return this._super();
|
||||
},
|
||||
resume: function () {
|
||||
return this.isCurrentStep('post-page') && !this.tour.ended();
|
||||
return (this.isCurrentStep('post-page') || this.isCurrentStep('publish-post')) && this._super();
|
||||
},
|
||||
trigger: function () {
|
||||
return (this.resume() && this.testUrl(/^\/blog\/[0-9]+\/\?enable_editor=1/)) || this._super();
|
||||
return (this.resume() && this.testUrl(/^\/blog\/[0-9]+\//)) || this._super();
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -167,13 +167,11 @@
|
|||
<t t-call="website.publish_management">
|
||||
<t t-set="object" t-value="blog_post"/>
|
||||
<t t-set="publish_edit" t-value="True"/>
|
||||
<li>
|
||||
<a href="#" t-attf-data-href="/blog/duplicate?blog_post_id=#{blog_post.id}">Duplicate</a>
|
||||
<script>
|
||||
var $a=$("[data-href$='/duplicate']");
|
||||
$a.attr("href", $a.data('href')).removeAttr('data-href');
|
||||
$a.next("script").remove();
|
||||
</script>
|
||||
<li t-ignore="True">
|
||||
<form class="duplicate hidden" action="/blog/duplicate">
|
||||
<input name="blog_post_id" t-att-value="blog_post.id"/>
|
||||
</form>
|
||||
<a href="#" class="duplicate" onclick="$(this).prev('form').submit()">Duplicate</a>
|
||||
</li>
|
||||
</t>
|
||||
</div>
|
||||
|
|
|
@ -23,10 +23,8 @@ from openerp import SUPERUSER_ID
|
|||
from openerp.addons.web import http
|
||||
from openerp.addons.web.http import request
|
||||
from openerp.tools.translate import _
|
||||
from openerp.addons import website_sale
|
||||
from openerp.addons.website.models import website
|
||||
from openerp.addons.website.controllers.main import Website as controllers
|
||||
from openerp.addons.website_sale.controllers.main import Ecommerce as Ecommerce
|
||||
controllers = controllers()
|
||||
|
||||
|
||||
|
@ -174,9 +172,9 @@ class website_event(http.Controller):
|
|||
def event(self, event=None, **post):
|
||||
if event.menu_id and event.menu_id.child_id:
|
||||
return request.redirect(event.menu_id.child_id[0].url)
|
||||
return request.redirect('/event/register/'+str(event.id))
|
||||
return request.redirect('/event/%s/register' % str(event.id))
|
||||
|
||||
@website.route(['/event/register/<model("event.event"):event>'], type='http', auth="public", multilang=True)
|
||||
@website.route(['/event/<model("event.event"):event>/register'], type='http', auth="public", multilang=True)
|
||||
def event_register(self, event=None, **post):
|
||||
values = {
|
||||
'event': event,
|
||||
|
@ -185,15 +183,14 @@ class website_event(http.Controller):
|
|||
return request.website.render("website_event.event_description_full", values)
|
||||
|
||||
@website.route(['/event/add_cart'], type='http', auth="public", multilang=True)
|
||||
def add_cart(self, event_id=None, **post):
|
||||
assert event_id, 'An event is required'
|
||||
def add_cart(self, event_id, **post):
|
||||
user_obj = request.registry['res.users']
|
||||
order_line_obj = request.registry.get('sale.order.line')
|
||||
ticket_obj = request.registry.get('event.event.ticket')
|
||||
|
||||
order = request.context['website_sale_order']
|
||||
if not order:
|
||||
order = website_sale.controllers.main.get_order()
|
||||
order = request.registry['website']._get_order(request.cr, request.uid, context=request.context)
|
||||
|
||||
partner_id = user_obj.browse(request.cr, SUPERUSER_ID, request.uid,
|
||||
context=request.context).partner_id.id
|
||||
|
|
|
@ -63,7 +63,7 @@ class event(osv.osv):
|
|||
'name': event.name
|
||||
}, context=context)
|
||||
tocreate = self._get_new_menu_pages(cr, uid, event, context)
|
||||
tocreate.append((_('Register'), '/event/register/'+str(event.id)))
|
||||
tocreate.append((_('Register'), '/event/%s/register' % str(event.id)))
|
||||
sequence = 0
|
||||
for name,url in tocreate:
|
||||
menuobj.create(cr, uid, {
|
||||
|
|
|
@ -20,8 +20,8 @@
|
|||
stepId: 'welcome-event',
|
||||
orphan: true,
|
||||
backdrop: true,
|
||||
title: "Event",
|
||||
content: "We will show how to create a new event.",
|
||||
title: "Create an Event",
|
||||
content: "Let's go through the firsts step to publish a new event.",
|
||||
template: self.popover({ next: "Start Tutorial", end: "Skip It" }),
|
||||
},
|
||||
{
|
||||
|
@ -29,21 +29,27 @@
|
|||
element: '#content-menu-button',
|
||||
placement: 'left',
|
||||
reflex: true,
|
||||
title: "Edit the content",
|
||||
content: "Click here to add content to your site.",
|
||||
title: "Add Content",
|
||||
content: "The <em>Content</em> menu allows to create new pages, events, menus, etc.",
|
||||
},
|
||||
{
|
||||
stepId: 'new-post-entry',
|
||||
element: 'a[data-action=new_event]',
|
||||
placement: 'left',
|
||||
title: "New event",
|
||||
content: "Click here to create an event.",
|
||||
title: "New Event",
|
||||
content: "Click here to create a new event.",
|
||||
triggers: function () {
|
||||
$(document).one('shown.bs.modal', function () {
|
||||
$('.modal button.btn-primary').click(function () {
|
||||
self.movetoStep('event-page');
|
||||
var $doc = $(document);
|
||||
function stopNewEvent () {
|
||||
self.stop();
|
||||
}
|
||||
$doc.on('hide.bs.modal', stopNewEvent);
|
||||
$doc.one('shown.bs.modal', function () {
|
||||
$('.modal button.btn-primary').one('click', function () {
|
||||
$doc.off('hide.bs.modal', stopNewEvent);
|
||||
self.moveToStep('event-page');
|
||||
});
|
||||
self.movetoStep('choose-category');
|
||||
self.moveToNextStep();
|
||||
});
|
||||
},
|
||||
},
|
||||
|
@ -51,33 +57,72 @@
|
|||
stepId: 'choose-name',
|
||||
element: '.modal input',
|
||||
placement: 'right',
|
||||
title: "Choose the event name",
|
||||
content: "Choose a name for the new event and click 'Continue'.",
|
||||
title: "Choose an Event Name",
|
||||
content: "Choose a name for your new event and click <em>'Continue'</em>. e.g: Technical Training",
|
||||
},
|
||||
{
|
||||
stepId: 'event-page',
|
||||
orphan: true,
|
||||
backdrop: true,
|
||||
title: "New event created",
|
||||
content: "You just created a new event. We are now going to edit it.",
|
||||
title: "New Event Created",
|
||||
content: "This is your new event page. We will edit the event presentation page.",
|
||||
template: self.popover({ next: "OK" }),
|
||||
},
|
||||
{
|
||||
stepId: 'add-block',
|
||||
stepId: 'edit-page',
|
||||
element: 'button[data-action=edit]',
|
||||
placement: 'bottom',
|
||||
title: "Edit the event desciption",
|
||||
content: "Edit the page to modify the event description.",
|
||||
triggers: function () {
|
||||
editor.on('tour:editor_bar_loaded', self, self.moveToNextStep);
|
||||
},
|
||||
},
|
||||
{
|
||||
stepId: 'add-image-text',
|
||||
element: 'button[data-action=snippet]',
|
||||
placement: 'bottom',
|
||||
reflex: true,
|
||||
title: "Layout your event",
|
||||
content: "Insert blocks like text-image to layout the body of your event.",
|
||||
triggers: function () {
|
||||
$('button[data-action=snippet]').one('click', function () {
|
||||
self.moveToNextStep();
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
stepId: 'drag-image-text',
|
||||
element: '#website-top-navbar [data-snippet-id=text-block].ui-draggable',
|
||||
placement: 'bottom',
|
||||
title: "Drag & Drop a block",
|
||||
content: "Drag the 'Text Block' block and drop it in your page.",
|
||||
triggers: function () {
|
||||
self.onSnippetDraggedAdvance('text-block');
|
||||
},
|
||||
},
|
||||
{
|
||||
stepId: 'save-changes',
|
||||
element: 'button[data-action=save]',
|
||||
placement: 'right',
|
||||
reflex: true,
|
||||
title: "Save your modifications",
|
||||
content: "Once you click on save, your event is updated.",
|
||||
},
|
||||
{
|
||||
stepId: 'publish-post',
|
||||
element: 'button.js_publish_btn',
|
||||
placement: 'right',
|
||||
reflex: true,
|
||||
title: "Publish your event",
|
||||
content: "Click to publish your event.",
|
||||
},
|
||||
];
|
||||
return this._super();
|
||||
},
|
||||
resume: function () {
|
||||
return this.isCurrentStep('event-page') && !this.tour.ended();
|
||||
return (this.isCurrentStep('event-page') || this.isCurrentStep('publish-post')) && this._super();
|
||||
},
|
||||
trigger: function () {
|
||||
return (this.resume() && this.testUrl(/^\/event\/[0-9]+\/\?enable_editor=1/)) || this._super();
|
||||
return (this.resume() && this.testUrl(/^\/event\/[0-9]+\/register/)) || this._super();
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -72,7 +72,7 @@
|
|||
</span>
|
||||
</t>
|
||||
<h4 class="media-heading">
|
||||
<a t-href="/event/#{(not event.menu_id) and 'register/' or ''}#{ event.id }/"><span t-field="event.name"> </span></a>
|
||||
<a t-href="/event/#{ event.id }/#{(not event.menu_id) and 'register/' or ''}"><span t-field="event.name"> </span></a>
|
||||
<small t-if="not event.website_published" class="label label-danger">not published</small>
|
||||
</h4>
|
||||
<div>
|
||||
|
|
|
@ -21,36 +21,36 @@
|
|||
|
||||
from openerp.addons.web import http
|
||||
from openerp.addons.web.http import request
|
||||
from openerp.tools.translate import _
|
||||
from openerp.addons import website_sale
|
||||
from openerp.addons.website.models import website
|
||||
from openerp.addons.website.controllers.main import Website as controllers
|
||||
|
||||
controllers = controllers()
|
||||
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from openerp import tools
|
||||
import urllib
|
||||
|
||||
class website_event(http.Controller):
|
||||
@website.route(['/event/track_view/<model("event.track"):track>'], type='http', auth="public", multilang=True)
|
||||
def event_track_view(self, track, **post):
|
||||
@website.route(['/event/<model("event.event"):event>/track/<model("event.track"):track>'], type='http', auth="public", multilang=True)
|
||||
def event_track_view(self, event, track, **post):
|
||||
# TODO: not implemented
|
||||
values = { 'track': track, 'event': track.event_id}
|
||||
return request.website.render("website_event_track.track_view", values)
|
||||
|
||||
@website.route(['/event/tracks/<model("event.event"):event>'], type='http', auth="public", multilang=True)
|
||||
@website.route([
|
||||
'/event/<model("event.event"):event>/track/',
|
||||
'/event/<model("event.event"):event>/track/tag/<model("event.track.tag"):tag>'
|
||||
], type='http', auth="public", multilang=True)
|
||||
def event_tracks(self, event, tag=None, **post):
|
||||
# TODO: filter on tracks: tags, search keywords
|
||||
if tag:
|
||||
track_obj = request.registry.get('event.track')
|
||||
track_ids = track_obj.search(request.cr, request.uid,
|
||||
[("id", "in", [track.id for track in event.track_ids]), ("tag_ids", "=", tag.id)], context=request.context)
|
||||
tracks = track_obj.browse(request.cr, request.uid, track_ids, context=request.context)
|
||||
else:
|
||||
tracks = event.track_ids
|
||||
values = {
|
||||
'event': event,
|
||||
'tracks': event.track_ids,
|
||||
'tracks': tracks,
|
||||
'tags': event.track_tag_ids,
|
||||
'searches': {}
|
||||
}
|
||||
print 'ICI'
|
||||
return request.website.render("website_event_track.tracks", values)
|
||||
|
||||
@website.route(['/event/detail/<model("event.event"):event>'], type='http', auth="public", multilang=True)
|
||||
|
@ -58,8 +58,12 @@ class website_event(http.Controller):
|
|||
values = { 'event': event }
|
||||
return request.website.render("website_event_track.event_home", values)
|
||||
|
||||
@website.route(['/event/track_proposal/<model("event.event"):event>'], type='http', auth="public", multilang=True)
|
||||
def event_detail(self, event=None, **post):
|
||||
@website.route(['/event/<model("event.event"):event>/track_proposal/'], type='http', auth="public", multilang=True)
|
||||
def event_track_proposal(self, event, **post):
|
||||
values = { 'event': event }
|
||||
return request.website.render("website_event_track.event_track_proposal", values)
|
||||
|
||||
@website.route(['/event/<model("event.event"):event>/track_proposal/success/'], type='http', auth="public", multilang=True)
|
||||
def event_track_proposal_success(self, event, **post):
|
||||
values = { 'event': event }
|
||||
return request.website.render("website_event_track.event_track_proposal_success", values)
|
||||
|
|
|
@ -148,7 +148,7 @@
|
|||
<xpath expr="//div[@class='oe_right oe_button_box']" position="inside">
|
||||
<button name="%(website_event_track.act_event_list_tracks)d" type="action" string="Tracks"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='twitter_hashtag']" position="after">
|
||||
<xpath expr="//field[@name='organizer_id']" position="after">
|
||||
<field name="track_tag_ids" widget="many2many_tags"/>
|
||||
</xpath>
|
||||
</field>
|
||||
|
|
|
@ -123,10 +123,9 @@ class event_event(osv.osv):
|
|||
context = context or {}
|
||||
result = super(event_event, self)._get_new_menu_pages(cr, uid, event, context=context)
|
||||
if event.show_tracks:
|
||||
result.append( (_('Agenda'), '/event/tracks/'+str(event.id)))
|
||||
result.append( (_('Agenda'), '/event/%s/track/' % event.id))
|
||||
if event.blog_id:
|
||||
result.append( (_('News'), '/blog/cat/'+str(event.blog_ig.id)))
|
||||
if event.show_track_proposal:
|
||||
result.append( (_('Talk Proposals'), '/event/track_proposal/'+str(event.id)))
|
||||
result.append( (_('Talk Proposals'), '/event/%s/track_proposal/' % event.id))
|
||||
return result
|
||||
|
||||
|
|
|
@ -1,4 +1,11 @@
|
|||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_event_tag_manager,event.tag manager,model_event_tag,event.group_event_user,1,1,1,1
|
||||
access_event_location_manager,event.track.location manager,model_event_track_location,event.group_event_user,1,1,1,1
|
||||
access_event_track_manager,event.track manager,model_event_track,event.group_event_user,1,1,1,1
|
||||
access_event_tag_public,event.tag public,model_event_tag,,1,0,0,0
|
||||
access_event_tag_manager,event.tag manager,model_event_tag,event.group_event_user,1,1,1,1
|
||||
access_event_track_stage_public,event.track.stage public,model_event_track_stage,,1,0,0,0
|
||||
access_event_track_stage_manager,event.track.stage manager,model_event_track_stage,event.group_event_user,1,1,1,1
|
||||
access_event_track_tag_public,event.track.tag public,model_event_track_tag,,1,0,0,0
|
||||
access_event_track_tag_manager,event.track.tag manager,model_event_track_tag,event.group_event_user,1,1,1,1
|
||||
access_event_track_location_public,event.track.location public,model_event_track_location,,1,0,0,0
|
||||
access_event_track_location_manager,event.track.location manager,model_event_track_location,event.group_event_user,1,1,1,1
|
||||
|
|
|
|
@ -43,10 +43,10 @@
|
|||
<div class="col-md-3 css_noprint">
|
||||
|
||||
<ul class="nav nav-pills nav-stacked">
|
||||
<li t-att-class="(not searches.get('tag')) and 'active' or ''"><a href="/event/tracks/#{ event.id }">All</a></li>
|
||||
<li t-att-class="'' if searches.get('tag') else 'active'"><a t-href="/event/#{ event.id }/track/">All</a></li>
|
||||
<t t-foreach="tags" t-as="tag">
|
||||
<li t-att-class="searches.get('tag') == tag.id and 'active' or ''">
|
||||
<a t-href="/event/tracks/#{ event.id }&tag=#{ tag.id }">
|
||||
<a t-href="/event/#{ event.id }/track/tag/#{ tag.id }">
|
||||
<t t-esc="tag.name"/>
|
||||
</a>
|
||||
</li>
|
||||
|
@ -59,7 +59,7 @@
|
|||
<li t-foreach="event.track_ids" t-as="track" class="media">
|
||||
<div class="media-body">
|
||||
<h4 class="media-heading">
|
||||
<a t-href="/event/track_view/#{track.id}"><span t-field="track.name"> </span></a>
|
||||
<a t-href="/event/#{ event.id }/track/#{track.id}"><span t-field="track.name"> </span></a>
|
||||
<!-- <small t-if="not track.website_published" class="label label-danger">not published</small> -->
|
||||
</h4>
|
||||
<p>TODO: NOT Implemented</p>
|
||||
|
@ -125,8 +125,7 @@
|
|||
</section>
|
||||
</div>
|
||||
<section id="forms" t-if="event.show_track_proposal">
|
||||
<form class="form-horizontal mt32" action="/event/track_proposal/success" method="post" enctype="multipart/form-data">
|
||||
<input type="hidden" t-att-value="event.id" name="event"/>
|
||||
<form class="form-horizontal mt32" action="/event/#{event.id}/track_proposal/success" method="post" enctype="multipart/form-data">
|
||||
<div class="form-group">
|
||||
<label class="col-md-3 col-sm-4 control-label" for="partner_name">Speaker Name</label>
|
||||
<div class="col-md-7 col-sm-8">
|
||||
|
@ -221,5 +220,14 @@
|
|||
<div class="oe_structure"/>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
|
||||
<template id="event_track_proposal_success">
|
||||
<t t-call="website_event.layout">
|
||||
<div class="container">
|
||||
Thanks
|
||||
</div>
|
||||
</t>
|
||||
</template>
|
||||
</data>
|
||||
</openerp>
|
||||
|
|
|
@ -16,29 +16,24 @@ class website_hr_recruitment(http.Controller):
|
|||
'/jobs/department/<model("hr.department"):department>',
|
||||
'/jobs/office/<model("res.partner"):office>'
|
||||
], type='http', auth="public", multilang=True)
|
||||
def jobs(self, department=None, office=None, page=0):
|
||||
hr_job_obj = request.registry['hr.job']
|
||||
domain = []
|
||||
jobpost_ids = hr_job_obj.search(request.cr, request.uid, domain, order="website_published desc,no_of_recruitment desc", context=request.context)
|
||||
jobs = hr_job_obj.browse(request.cr, request.uid, jobpost_ids, request.context)
|
||||
def jobs(self, department=None, office=None):
|
||||
jobs = self._browse_jobs([])
|
||||
|
||||
departments = set()
|
||||
offices = set()
|
||||
for job in jobs:
|
||||
if job.department_id:
|
||||
departments.add(job.department_id)
|
||||
|
||||
offices = set()
|
||||
for job in jobs:
|
||||
if job.address_id:
|
||||
offices.add(job.address_id)
|
||||
|
||||
if department or office:
|
||||
if office:
|
||||
domain += [('address_id','=', office.id)]
|
||||
domain = []
|
||||
if department:
|
||||
domain += [('department_id','=', department.id)]
|
||||
jobpost_ids = hr_job_obj.search(request.cr, request.uid, domain, order="website_published desc,no_of_recruitment desc", context=request.context)
|
||||
jobs = hr_job_obj.browse(request.cr, request.uid, jobpost_ids, request.context)
|
||||
domain.append(('department_id','=', department.id))
|
||||
if office:
|
||||
domain.append(('address_id','=', office.id))
|
||||
jobs = self._browse_jobs(domain)
|
||||
|
||||
return request.website.render("website_hr_recruitment.index", {
|
||||
'jobs': jobs,
|
||||
|
@ -52,7 +47,7 @@ class website_hr_recruitment(http.Controller):
|
|||
def detail(self, job, **kwargs):
|
||||
return request.website.render("website_hr_recruitment.detail", {'job': job})
|
||||
|
||||
@website.route(['/job/success'], type='http', auth="admin", multilang=True)
|
||||
@website.route(['/job/success'], methods=['POST'], type='http', auth="admin", multilang=True)
|
||||
def success(self, **post):
|
||||
data = {
|
||||
'name': _('Online Form'),
|
||||
|
@ -69,7 +64,7 @@ class website_hr_recruitment(http.Controller):
|
|||
try:
|
||||
model, source_id = imd.get_object_reference(request.cr, request.uid, 'hr_recruitment', 'source_website_company')
|
||||
data['source_id'] = source_id
|
||||
except ValueError, e:
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
jobid = request.registry['hr.applicant'].create(request.cr, request.uid, data, context=request.context)
|
||||
|
@ -79,15 +74,20 @@ class website_hr_recruitment(http.Controller):
|
|||
'datas': base64.encodestring(post['ufile'].read()),
|
||||
'datas_fname': post['ufile'].filename,
|
||||
'res_model': 'hr.applicant',
|
||||
'res_name': post['name'],
|
||||
'res_name': data['partner_name'],
|
||||
'res_id': jobid
|
||||
}
|
||||
request.registry['ir.attachment'].create(request.cr, request.uid, attachment_values, context=request.context)
|
||||
return request.website.render("website_hr_recruitment.thankyou", {})
|
||||
|
||||
@website.route(['/job/apply'], type='http', auth="public", multilang=True)
|
||||
def applyjobpost(self, job=None):
|
||||
return request.website.render("website_hr_recruitment.applyjobpost", { 'job': job })
|
||||
def applyjobpost(self, job):
|
||||
[job_object] = request.registry['hr.job'].browse(
|
||||
request.cr, request.uid, [int(job)], context=request.context)
|
||||
|
||||
return request.website.render("website_hr_recruitment.applyjobpost", {
|
||||
'job': job_object
|
||||
})
|
||||
|
||||
@website.route('/job/publish', type='json', auth="admin", multilang=True)
|
||||
def publish(self, id, object):
|
||||
|
@ -102,3 +102,11 @@ class website_hr_recruitment(http.Controller):
|
|||
}, context=request.context)
|
||||
|
||||
return request.redirect("/job/detail/%s/?enable_editor=1" % job_id)
|
||||
|
||||
def _browse_jobs(self, domain):
|
||||
Jobs = request.registry['hr.job']
|
||||
jobpost_ids = Jobs.search(request.cr, request.uid, domain,
|
||||
order="website_published desc,no_of_recruitment desc",
|
||||
context=request.context)
|
||||
jobs = Jobs.browse(request.cr, request.uid, jobpost_ids, context=request.context)
|
||||
return jobs
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
(function() {
|
||||
"use strict";
|
||||
|
||||
var website = openerp.website;
|
||||
website.add_template_file('/website_hr_recruitment/static/src/xml/website_hr_recruitment.xml');
|
||||
openerp.website.add_template_file(
|
||||
'/website_hr_recruitment/static/src/xml/website_hr_recruitment.xml');
|
||||
})();
|
||||
|
|
|
@ -86,7 +86,7 @@
|
|||
</div>
|
||||
|
||||
<div class="oe_structure">
|
||||
<section data-snippet-id="cta" class="oe_dark">
|
||||
<section data-snippet-id="cta" class="oe_dark mb32">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-12 text-center mt16 mb16">
|
||||
|
@ -138,7 +138,7 @@
|
|||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-12 text-center mt16 mb16">
|
||||
<a t-href="/job/apply/?job=#{ job.id }/" class="btn btn-primary btn-lg">Apply</a>
|
||||
<a t-href="/job/apply/?job=#{ job.id }" class="btn btn-primary btn-lg">Apply</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
<div class="input-group js_follow" t-att-data-id="object.id"
|
||||
t-att-data-object="object._name"
|
||||
t-att-data-follow="object.id and object.message_is_follower and 'on' or 'off'">
|
||||
<input type="email" name="email"
|
||||
<input t-if="is_public_user"
|
||||
type="email" name="email"
|
||||
class="js_follow_email form-control"
|
||||
t-att-value="email or ''"
|
||||
placeholder="your email..."/>
|
||||
|
|
|
@ -9,7 +9,7 @@ from openerp.addons.web.http import request
|
|||
from openerp.addons.website.models import website
|
||||
|
||||
|
||||
class CheckoutInfo:
|
||||
class CheckoutInfo(object):
|
||||
mandatory_billing_fields = ["name", "phone", "email", "street", "city", "country_id", "zip"]
|
||||
optional_billing_fields = ["company", "state_id"]
|
||||
string_billing_fields = ["name", "phone", "email", "street", "city", "zip"]
|
||||
|
@ -27,7 +27,7 @@ class CheckoutInfo:
|
|||
return self.mandatory_fields() + self.optional_fields()
|
||||
|
||||
def empty(self):
|
||||
return dict((field_name, '') for field_name in self.all_fields())
|
||||
return dict.fromkeys(self.all_fields(), '')
|
||||
|
||||
def from_partner(self, partner):
|
||||
result = dict((field_name, getattr(partner, field_name)) for field_name in self.string_billing_fields if getattr(partner, field_name))
|
||||
|
@ -464,6 +464,8 @@ class Ecommerce(http.Controller):
|
|||
|
||||
if 'promo' in post:
|
||||
self.change_pricelist(post.get('promo'))
|
||||
else:
|
||||
self.get_pricelist()
|
||||
|
||||
suggested_ids = []
|
||||
product_ids = []
|
||||
|
@ -489,17 +491,18 @@ class Ecommerce(http.Controller):
|
|||
return request.website.render("website_sale.mycart", values)
|
||||
|
||||
@website.route(['/shop/add_cart/'], type='http', auth="public", multilang=True)
|
||||
def add_cart(self, path=None, product_id=None, order_line_id=None, remove=None, **kw):
|
||||
def add_cart(self, product_id=None, order_line_id=None, remove=None, **kw):
|
||||
self.add_product_to_cart(product_id=product_id and int(product_id), order_line_id=order_line_id and int(order_line_id), number=(remove and -1 or 1))
|
||||
return request.redirect("/shop/mycart/")
|
||||
|
||||
@website.route(['/shop/add_cart_json/'], type='json', auth="public")
|
||||
def add_cart_json(self, product_id=None, order_line_id=None, remove=None):
|
||||
quantity = self.add_product_to_cart(product_id=product_id, order_line_id=order_line_id, number=(remove and -1 or 1))
|
||||
order = self.registry['website'].get_current_order(request.cr, request.uid, context=request.context)
|
||||
return [quantity, order.get_total_quantity(), order.amount_total, request.website.render("website_sale.total", {
|
||||
'website_sale_order': order
|
||||
}).strip()]
|
||||
order = request.registry['website'].get_current_order(request.cr, request.uid, context=request.context)
|
||||
return [quantity,
|
||||
order.get_total_quantity(),
|
||||
order.amount_total,
|
||||
request.website._render("website_sale.total", {'website_sale_order': order})]
|
||||
|
||||
@website.route(['/shop/set_cart_json/'], type='json', auth="public")
|
||||
def set_cart_json(self, path=None, product_id=None, order_line_id=None, set_number=0, json=None):
|
||||
|
@ -518,6 +521,8 @@ class Ecommerce(http.Controller):
|
|||
tx = context.get('website_sale_transaction')
|
||||
if tx and tx.state != 'draft':
|
||||
return request.redirect('/shop/payment/confirmation/%s' % order.id)
|
||||
|
||||
self.get_pricelist()
|
||||
|
||||
orm_partner = registry.get('res.partner')
|
||||
orm_user = registry.get('res.users')
|
||||
|
|
|
@ -39,11 +39,17 @@
|
|||
title: "Create a new product",
|
||||
content: "Select 'New Product' to create it and manage its properties to boost your sales.",
|
||||
triggers: function () {
|
||||
$(document).one('shown.bs.modal', function () {
|
||||
$('.modal button.btn-primary').click(function () {
|
||||
self.movetoStep('product-page');
|
||||
var $doc = $(document);
|
||||
function stopProductTour () {
|
||||
self.stop();
|
||||
}
|
||||
$doc.on('hide.bs.modal', stopProductTour);
|
||||
$doc.one('shown.bs.modal', function () {
|
||||
$('.modal button.btn-primary').one('click', function () {
|
||||
$doc.off('hide.bs.modal', stopProductTour);
|
||||
self.moveToStep('product-page');
|
||||
});
|
||||
self.movetoStep('enter-name');
|
||||
self.moveToNextStep();
|
||||
});
|
||||
},
|
||||
},
|
||||
|
@ -52,7 +58,7 @@
|
|||
element: '.modal input[type=text]',
|
||||
placement: 'right',
|
||||
title: "Choose name",
|
||||
content: "Enter a name for your new product.",
|
||||
content: "Enter a name for your new product then click 'Continue'.",
|
||||
|
||||
},
|
||||
{
|
||||
|
@ -71,22 +77,89 @@
|
|||
content: "Edit the sale price of this product by clicking on the amount. The price is the sale price used in all sale order when selling this product.",
|
||||
template: self.popover({ next: "OK" }),
|
||||
},
|
||||
{
|
||||
stepId: 'update-image',
|
||||
element: '#wrap img.img:first',
|
||||
placement: 'top',
|
||||
title: "Update image",
|
||||
content: "Click here to set an image describing your product.",
|
||||
triggers: function () {
|
||||
function registerClick () {
|
||||
$('button.image-edit-button').one('click', function () {
|
||||
$('#wrap img.img:first').off('hover', registerClick);
|
||||
self.moveToNextStep();
|
||||
});
|
||||
}
|
||||
$('#wrap img.img:first').on('hover', registerClick);
|
||||
|
||||
},
|
||||
},
|
||||
{
|
||||
stepId: 'upload-image',
|
||||
element: 'button.filepicker',
|
||||
placement: 'left',
|
||||
title: "Upload image",
|
||||
content: "Click on 'Upload an image from your computer' to pick an image describing your product.",
|
||||
template: self.popover({ next: "OK" }),
|
||||
triggers: function () {
|
||||
$(document).on('hide.bs.modal', function () {
|
||||
self.moveToStep('add-block');
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
stepId: 'save-image',
|
||||
element: 'button.save',
|
||||
placement: 'right',
|
||||
title: "Save the image",
|
||||
content: "Click 'Save Changes' to add the image to the product decsription.",
|
||||
},
|
||||
{
|
||||
stepId: 'add-block',
|
||||
element: 'button[data-action=snippet]',
|
||||
placement: 'bottom',
|
||||
reflex: true,
|
||||
title: "Describe the product for your audience",
|
||||
content: "Insert blocks like text-image, or gallery to fully describe the product and make your visitors want to buy this product.",
|
||||
triggers: function () {
|
||||
$('button[data-action=snippet]').one('click', function () {
|
||||
self.moveToNextStep();
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
stepId: 'drag-big-picture',
|
||||
element: '#website-top-navbar [data-snippet-id=big-picture].ui-draggable',
|
||||
placement: 'bottom',
|
||||
title: "Drag & Drop a block",
|
||||
content: "Drag the 'Big Picture' block and drop it in your page.",
|
||||
triggers: function () {
|
||||
self.onSnippetDraggedAdvance('big-picture');
|
||||
},
|
||||
},
|
||||
{
|
||||
stepId: 'save-changes',
|
||||
element: 'button[data-action=save]',
|
||||
placement: 'right',
|
||||
reflex: true,
|
||||
title: "Save your modifications",
|
||||
content: "Once you click on save, your product is updated.",
|
||||
},
|
||||
{
|
||||
stepId: 'publish-product',
|
||||
element: 'button.js_publish_btn',
|
||||
placement: 'right',
|
||||
reflex: true,
|
||||
title: "Publish your product",
|
||||
content: "Click to publish your product so your customers can see it.",
|
||||
},
|
||||
];
|
||||
return this._super();
|
||||
},
|
||||
resume: function () {
|
||||
return this.isCurrentStep('product-page') && !this.tour.ended();
|
||||
return (this.isCurrentStep('product-page') || this.isCurrentStep('publish-product')) && this._super();
|
||||
},
|
||||
trigger: function (url) {
|
||||
return (this.resume() && this.testUrl(/^\/shop\/product\/[0-9]+\/\?enable_editor=1/)) || this._super();
|
||||
return (this.resume() && this.testUrl(/^\/shop\/product\/[0-9]+\//)) || this._super();
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -292,7 +292,6 @@
|
|||
|
||||
<div class="product_price mt16" t-if="product.product_variant_ids">
|
||||
<h4>
|
||||
<b><span class="oe_price" t-esc="product.product_variant_ids[0].price" /> €</b>
|
||||
<t t-if="product.product_variant_ids[0].lst_price != product.product_variant_ids[0].price">
|
||||
<span class="text-danger" style="text-decoration: line-through;"
|
||||
t-field="product.product_variant_ids[0].lst_price"
|
||||
|
@ -886,7 +885,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="js_payment mb64" t-if="not payment_acquirer_id and payments">
|
||||
<div class="js_payment mb64" t-if="not payment_acquirer_id and payments" id="payment_method">
|
||||
<p>Payment method:</p>
|
||||
<div>
|
||||
<t t-foreach="payments or []" t-as="payment">
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<xpath expr="//t[@t-set='head']" position="inside">
|
||||
<script type="text/javascript" src="/website_sale_delivery/static/src/js/website_sale_delivery.js"></script>
|
||||
</xpath>
|
||||
<xpath expr="//div[@id='js_payment']" position="before">
|
||||
<xpath expr="//div[@id='payment_method']" position="before">
|
||||
|
||||
<t t-if="len(deliveries)">
|
||||
<p>Choose Your Delivery method:</p>
|
||||
|
|
Loading…
Reference in New Issue