diff --git a/addons/event_sale/event_sale.py b/addons/event_sale/event_sale.py index 8bacd37fc15..a8f73a3055f 100644 --- a/addons/event_sale/event_sale.py +++ b/addons/event_sale/event_sale.py @@ -29,14 +29,17 @@ class product(osv.osv): 'event_type_id': fields.many2one('event.type', 'Type of Event', help='Select event types so when we use this product in sales order lines, it will filter events of this type only.'), } - def onchange_event_ok(self, cr, uid, ids, event_ok, context=None): - return {'value': {'type': event_ok and 'service' or False}} + def onchange_event_ok(self, cr, uid, ids, type, event_ok, context=None): + return {'value': {'type': event_ok and 'service' or type != 'service' and type or False}} class sale_order_line(osv.osv): _inherit = 'sale.order.line' _columns = { - 'event_id': fields.many2one('event.event', 'Event', help="Choose an event and it will automatically create a registration for this event."), + 'event_id': fields.many2one('event.event', 'Event', + help="Choose an event and it will automatically create a registration for this event."), + 'event_ticket_id': fields.many2one('event.event.ticket', 'Event Ticket', + help="Choose an event ticket and it will automatically create a registration for this event ticket."), #those 2 fields are used for dynamic domains and filled by onchange 'event_type_id': fields.related('product_id','event_type_id', type='many2one', relation="event.type", string="Event Type"), 'event_ok': fields.related('product_id', 'event_ok', string='event_ok', type='boolean'), @@ -73,9 +76,8 @@ class sale_order_line(osv.osv): if context is None: context = {} registration_obj = self.pool.get('event.registration') - sale_obj = self.pool.get('sale.order') for order_line in self.browse(cr, uid, ids, context=context): - if order_line.event_id.id: + if order_line.event_id: dic = { 'name': order_line.order_id.partner_invoice_id.name, 'partner_id': order_line.order_id.partner_id.id, @@ -84,9 +86,131 @@ class sale_order_line(osv.osv): 'phone': order_line.order_id.partner_id.phone, 'origin': order_line.order_id.name, 'event_id': order_line.event_id.id, + 'event_ticket_id': order_line.event_ticket_id and order_line.event_ticket_id.id or None, } - message = _("The registration has been created for event %s from the Sale Order %s. ") % (order_line.event_id.name, order_line.order_id.name) + print dic + + if order_line.event_ticket_id: + if order_line.event_ticket_id.register_avail != 9999 and dic['nb_register'] > order_line.event_ticket_id.register_avail: + raise osv.except_osv(_('Error!'), _('There are not enough tickets available (%s) for %s' % (order_line.event_ticket_id.register_avail, order_line.event_ticket_id.name))) + message = _("The registration has been created for event %s with the ticket %s from the Sale Order %s. ") % (order_line.event_id.name, order_line.event_ticket_id.name, order_line.order_id.name) + else: + message = _("The registration has been created for event %s from the Sale Order %s. ") % (order_line.event_id.name, order_line.order_id.name) + context.update({'mail_create_nolog': True}) registration_id = registration_obj.create(cr, uid, dic, context=context) registration_obj.message_post(cr, uid, [registration_id], body=message, context=context) return super(sale_order_line, self).button_confirm(cr, uid, ids, context=context) + + def onchange_event_ticket_id(self, cr, uid, ids, event_ticket_id=False, context=None): + price = event_ticket_id and self.pool.get("event.event.ticket").browse(cr, uid, event_ticket_id, context=context).price or False + return {'value': {'price_unit': price}} + + +class event_event(osv.osv): + _inherit = 'event.event' + + def _get_register_max(self, cr, uid, ids, field_name, arg, context=None): + result = dict.fromkeys(ids, 0) + for rec in self.browse(cr, uid, ids, context=context): + result[rec.id] = sum([ep.register_max for ep in rec.event_ticket_ids]) + return result + + _columns = { + 'website_published': fields.boolean('Available in the website'), + 'description_website': fields.html('Description for the website'), + 'event_ticket_ids': fields.one2many('event.event.ticket', "event_id", "Event Ticket"), + 'organizer_id': fields.many2one('res.partner', "Orgonizer"), + 'phone': fields.related('orgonizer_id', 'phone', type='char', string='Phone'), + 'email': fields.related('orgonizer_id', 'email', type='char', string='Email'), + 'register_max': fields.function(_get_register_max, + string='Maximum Registrations', + help="The maximum registration level is equal to the sum of the maximum registration of event ticket." + + "If you have too much registrations you are not able to confirm your event. (0 to ignore this rule )", + type='integer', store=True) + } + + def check_registration_limits(self, cr, uid, ids, context=None): + for event in self.browse(cr, uid, ids, context=context): + if event.event_ticket_ids: + event.event_ticket_ids.check_registration_limits_before(0) + return super(event_event, self).check_registration_limits(cr, uid, ids, context=context) + + +class event_ticket(osv.osv): + _name = 'event.event.ticket' + + def _get_register(self, cr, uid, ids, fields, args, context=None): + """Get Confirm or uncofirm register value. + @param ids: List of Event Ticket registration type's id + @param fields: List of function fields(register_current and register_prospect). + @param context: A standard dictionary for contextual values + @return: Dictionary of function fields value. + """ + res = {} + for event in self.browse(cr, uid, ids, context=context): + res[event.id] = {} + reg_open = reg_done = reg_draft =0 + for registration in event.registration_ids: + if registration.state == 'open': + reg_open += registration.nb_register + elif registration.state == 'done': + reg_done += registration.nb_register + elif registration.state == 'draft': + reg_draft += registration.nb_register + for field in fields: + number = 0 + if field == 'register_current': + number = reg_open + elif field == 'register_attended': + number = reg_done + elif field == 'register_prospect': + number = reg_draft + elif field == 'register_avail': + #the number of ticket is unlimited if the event.register_max field is not set. + #In that cas we arbitrary set it to 9999, it is used in the kanban view to special case the display of the 'subscribe' button + number = event.register_max - reg_open if event.register_max != 0 else 9999 + res[event.id][field] = number + return res + + _columns = { + 'name': fields.char('Name', size=64, required=True), + 'event_id': fields.many2one('event.event', "Event", required=True), + 'product_id': fields.many2one('product.product', 'Product', required=True, domain=[("event_type_id", "!=", False)]), + 'registration_ids': fields.one2many('event.registration', 'event_ticket_id', 'Registrations'), + 'deadline': fields.date("Sales End"), + 'price': fields.float('Price'), + 'register_max': fields.integer('Maximum Registrations'), + 'register_current': fields.function(_get_register, string='Current Registrations', type='integer', multi='register_numbers'), + 'register_avail': fields.function(_get_register, string='Available Registrations', type='integer', multi='register_numbers'), + 'register_prospect': fields.function(_get_register, string='Unconfirmed Registrations', type='integer', multi='register_numbers'), + 'register_attended': fields.function(_get_register, string='# of Participations', type='integer', multi='register_numbers'), + } + + def check_registration_limits_before(self, cr, uid, ids, number, context=None): + for ticket in self.browse(cr, uid, ids, context=context): + if ticket.register_max: + if not ticket.register_avail: + raise osv.except_osv(_('Warning!'),_('No Tickets Available for "%s"' % ticket.name)) + elif number + ticket.register_current > ticket.register_max: + raise osv.except_osv(_('Warning!'), _('There only %d tickets available for "%s"' % (ticket.register_avail, ticket.name))) + return True + + def onchange_product_id(self, cr, uid, ids, product_id=False, context=None): + return {'value': {'price': self.pool.get("product.product").browse(cr, uid, product_id).list_price or 0}} + + +class event_registration(osv.osv): + """Event Registration""" + _inherit= 'event.registration' + _columns = { + 'event_ticket_id': fields.many2one('event.event.ticket', 'Event Ticket'), + } + + def registration_open(self, cr, uid, ids, context=None): + """ Open Registration + """ + for registration in self.browse(cr, uid, ids, context=context): + if registration.event_ticket_id: + registration.event_ticket_id.check_registration_limits_before(1) + return super(event_registration, self).registration_open(cr, uid, ids, context=context) diff --git a/addons/event_sale/event_sale_view.xml b/addons/event_sale/event_sale_view.xml index cc00365c980..43d2fcde495 100644 --- a/addons/event_sale/event_sale_view.xml +++ b/addons/event_sale/event_sale_view.xml @@ -7,7 +7,7 @@
- +
@@ -23,6 +23,8 @@ + @@ -34,5 +36,33 @@ + + view_event_form + event.event + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/addons/hr_recruitment/static/description/index.html b/addons/hr_recruitment/static/description/index.html new file mode 100644 index 00000000000..f91da54ad55 --- /dev/null +++ b/addons/hr_recruitment/static/description/index.html @@ -0,0 +1,115 @@ +
+
+
+

Open Source Job Board

+

Organize your job postings and applications

+
+
+
+ + + + +
+
+
+

+ Organize your job board, promote your job announces and keep + track of application submissions easily. Follow every applicant + and build up a database of skills and profiles with indexed + documents. +

+ +
+
+
+ +
+
+

Post Your Jobs on Best Job Boards

+

LinkedIn, Monster, Kraigslist, Careerbuilder,...

+
+ +
+
+

+ Connect automatically to most famous job board websites; + linkedIn, Monster, Kraigslist, ... Every job position has a new + email address automatically assigned to route applications + automatically to the right job position. +

+ Whether applicants contact you by email or using an online + form, you get all the data indexed automatically (resumes, + motivation letter) and you can answer in just a click, reusing + templates of answers. +

+
+
+
+ +
+
+

Customize Your Recruitment Process

+

Define your own stages and interviewers

+
+

+ Use the kanban view and customize the steps of your recruitments + process; pre-qualification, first interview, second interview, + negociaiton, ... +

+ Get accurate statistics on your recruitment pipeline. Get reports + to compare the performance of your different investments on + external job boards. +

+
+
+ +
+
+
+ + +
+
+

Streamline Your Recruitment Process

+

Index resumes, track applicants, search profiles

+
+ +
+
+

+ Follow applicants in your recruitment process with the smart + kanban view. Save time by automating some communications with + email templates. +

+ Documents like resumes and motivation letters are indexed + automatically, allowing you to easily find for specific skills + and build up a database of profiles. +

+
+
+
+ + +
+
+

Integrated Surveys

+

Define your own online or offline surveys

+
+
+ +
+
+
+

+ Create your own interview canvas based on our best practices. + Use the survey designer to adapt questions to your own process. + Ask the applicant to fill in the survey online, or the + interviewer to use it during real interviews. +

+
+
+
+ diff --git a/addons/project/static/description/index.html b/addons/project/static/description/index.html index 2a1574c5d68..cd8deb1d0ec 100644 --- a/addons/project/static/description/index.html +++ b/addons/project/static/description/index.html @@ -42,7 +42,6 @@ allowing teams to optimize their job.

Gantt Charts

-

Calendar

diff --git a/addons/website/__init__.py b/addons/website/__init__.py index be415754735..17879db9942 100644 --- a/addons/website/__init__.py +++ b/addons/website/__init__.py @@ -1,3 +1,5 @@ import controllers import res_config -from website import website \ No newline at end of file + +from website import website +import view diff --git a/addons/website/__openerp__.py b/addons/website/__openerp__.py index 3cdb3230e5d..aa0f5dbf64f 100644 --- a/addons/website/__openerp__.py +++ b/addons/website/__openerp__.py @@ -1,6 +1,7 @@ { 'name': 'Website', - 'category': 'CMS', + 'category': 'Website', + 'summary': 'Create Your Enterprise Website', 'version': '1.0', 'description': """ OpenERP Website CMS @@ -14,13 +15,5 @@ OpenERP Website CMS 'views/views.xml', 'views/res_config.xml', 'website_data.xml', - ], - 'js': [ - 'static/lib/bootstrap/js/bootstrap.js', - 'static/src/js/website.js', ], - 'css': [ - 'static/src/css/editor.css', - ], - 'qweb': ['static/src/xml/*.xml'], } diff --git a/addons/website/controllers/main.py b/addons/website/controllers/main.py index a347f789f7c..bd3f7285126 100644 --- a/addons/website/controllers/main.py +++ b/addons/website/controllers/main.py @@ -2,21 +2,31 @@ import base64 import json import logging -import urllib import cStringIO -from PIL import Image, ImageOps +from PIL import Image import openerp from openerp.addons.web import http -from openerp.addons.web.controllers.main import manifest_list from openerp.addons.web.http import request import werkzeug import werkzeug.exceptions import werkzeug.wrappers +import hashlib +import os logger = logging.getLogger(__name__) + +def auth_method_public(): + registry = openerp.modules.registry.RegistryManager.get(request.db) + if not request.session.uid: + request.uid = registry['website'].get_public_user().id + else: + request.uid = request.session.uid +http.auth_methods['public'] = auth_method_public + + class Website(openerp.addons.web.controllers.main.Home): @http.route('/', type='http', auth="admin") def index(self, **kw): @@ -52,7 +62,7 @@ class Website(openerp.addons.web.controllers.main.Home): return werkzeug.utils.redirect("/page/%s" % path) @http.route('/page/', type='http', auth="admin") - def page(self, path): + def page(self, path, **kwargs): website = request.registry.get("website") values = website.get_rendering_context({ 'path': path @@ -63,6 +73,45 @@ class Website(openerp.addons.web.controllers.main.Home): html = website.render('website.404', values) return html + @http.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) + if view.inherit_id: + print '*', view.inherit_id + value = False + else: + value = view.inherit_option_id and view.inherit_option_id.id or False + print '*', view.inherit_id, 'no', value, view + view_obj.write(request.cr, request.uid, [view_id], { + 'inherit_id': value + }, context=request.context) + print 'Wrote', value, 'on', view_id + return True + + @http.route('/website/customize_template_get', type='json', auth='admin') # FIXME: auth + def customize_template_get(self, xml_id): + 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: + if v.inherit_option_id.id not in done: + result.append({ + 'name': v.inherit_option_id.name, + 'header': True, + 'active': False + }) + done[v.inherit_option_id.id] = True + result.append({ + 'name': v.name, + 'id': v.id, + 'header': False, + 'active': v.inherit_id.id == v.inherit_option_id.id + }) + return result + @http.route('/website/attach', type='http', auth='admin') # FIXME: auth def attach(self, CKEditorFuncNum, CKEditor, langCode, upload): req = request.httprequest @@ -107,4 +156,56 @@ class Website(openerp.addons.web.controllers.main.Home): return response + @http.route('/website/image', type='http', auth="public") + def image(self, model, id, field, **kw): + last_update = '__last_update' + Model = request.registry[model] + headers = [('Content-Type', 'image/png')] + etag = request.httprequest.headers.get('If-None-Match') + hashed_session = hashlib.md5(request.session_id).hexdigest() + retag = hashed_session + try: + if etag: + date = Model.read(request.cr, request.uid, [id], [last_update], request.context)[0].get(last_update) + if hashlib.md5(date).hexdigest() == etag: + return werkzeug.wrappers.Response(status=304) + + res = Model.read(request.cr, request.uid, [id], [last_update, field], request.context)[0] + retag = hashlib.md5(res.get(last_update)).hexdigest() + image_base64 = res.get(field) + + if kw.get('resize'): + resize = kw.get('resize').split(',') + if len(resize) == 2 and int(resize[0]) and int(resize[1]): + width = int(resize[0]) + height = int(resize[1]) + # resize maximum 500*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) + except Exception: + image_data = open(os.path.join(http.addons_manifest['web']['addons_path'], 'web', 'static', 'src', 'img', 'placeholder.png'), 'rb').read() + + headers.append(('ETag', retag)) + headers.append(('Content-Length', len(image_data))) + try: + ncache = int(kw.get('cache')) + headers.append(('Cache-Control', 'no-cache' if ncache == 0 else 'max-age=%s' % (ncache))) + except: + pass + return request.make_response(image_data, headers) + + @http.route(['/website/publish/'], type='http', auth="public") + def publish(self, **post): + _id = int(post['id']) + _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}) + obj = _object.browse(request.cr, request.uid, _id) + + return obj.website_published and "1" or "0" + # vim:expandtab:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/website/static/description/index.html b/addons/website/static/description/index.html index b0c9a52569e..af0829ce38d 100644 --- a/addons/website/static/description/index.html +++ b/addons/website/static/description/index.html @@ -2,7 +2,7 @@

Enterprise Website Builder

-

Get an awesome website. Customize easily.

+

Get an awesome website, easily customizable

@@ -14,9 +14,10 @@

-Create enterprise-grade website with our super easy builder; no backend, everything -is editable inline. Benefit from out-of-the-box business features; shop, events, blogs, -jobs announces, "our team" pages, etc. + Create enterprise grade website with our super easy builder; no + backend, everything is editable inline. Benefit from + out-of-the-box business features; ecommerce, events, blogs, + jobs announces, our references, "our team" page, call-to-actions, etc.

Create your free website @@ -66,6 +67,9 @@ jobs announces, "our team" pages, etc. Whether it's for products descriptions, blogs or static pages, you don't need to be a professioanl designer to create modern and clean web pages. +

+ Get rid of traditional WYSIWYG editors that does not allow to + design clean and mobile friendly pages.

@@ -98,8 +102,53 @@ jobs announces, "our team" pages, etc.
-

Multi-languages Without Pain

-

Automate updates and transaltions easily

+

A Great Mobile Experience

+

Responsive content on all pages

+
+
+ +
+
+
+

+ Get a mobile friendly website thanks to our responsive design + based on bootstrap. The layout of every page and content adapts + automatically to the screen size. (mobile phones, tablets, desktop) + You don't have to worry about mobile contents, it works by default. +

+ Our building blocks allows to reuse responsive components to + structure your website pages, which is not possible with + traditional WYSIWYG editors. +

+
+
+
+ +
+
+

Optimize your online presence efficiently

+

SEO tools at your finger tips

+
+

+ SEO tools are ready to use, with no configuration required. + OpenERP suggests keywords according to Google most searched + terms, Google Analytics tracks your shopping cart events, + sitemap and structuered content are created automatically for + Google indexation, etc. +

+
+
+
+ +
+
+
+
+ +
+
+

Multilanguage Without Pain

+

Propagate changes and automate translation updates

@@ -109,13 +158,13 @@ jobs announces, "our team" pages, etc.

Get your website translated in multiple languages with no effort. OpenERP proposes and propagates translations automatically - across pages. + across pages, following what you edit on the master page.

Our translation "on demand" features allows you to benefit from - professional translators to translate all your changes + professional translators to translate all your contents automatically. Just change any part of your website (a new blog - post, a new pages, updated products, ...) and the translated - versions are updated automatically in around 32 hours. + post, new pages, updated products, ...) and the translated + versions are updated automatically in around 24 hours.

@@ -144,23 +193,49 @@ jobs announces, "our team" pages, etc.
-
+
-

Optimize your online presence efficiently

-

SEO tools at your finger tips

-
-

- SEO tools are ready to use, with no configuration required. - OpenERP suggests keywords according to Google most searched - terms, Google Analytics tracks your shopping cart events, - sitemap and structuered content are created automatically for - Google indexation, etc. -

+

Fully Integrated With Others Apps

+

Get hundreds of open source apps for free

+
+ +

eCommerce

+
+ +
+
+

+ Promote products, sell online, optimize visitors' shopping experiences. +

-
-
- -
+
+ +

Blogs

+
+ +
+
+

+ Write news, attract new visitors, build customer loyalty. +

+
+
+ +

Online Events

+
+ +
+
+

+ Schedule, organize, promote or sell events online; conferences, + webinars, trainings, etc. +

+
+
+ diff --git a/addons/website/static/src/css/Makefile b/addons/website/static/src/css/Makefile index 238219f8906..2c05352f1e1 100644 --- a/addons/website/static/src/css/Makefile +++ b/addons/website/static/src/css/Makefile @@ -1,3 +1,4 @@ sass: sass -t expanded --unix-newlines --watch website.sass:website.css& + sass -t expanded --unix-newlines --watch website-flatly.sass:website-flatly.css& sass -t expanded --unix-newlines --watch editor.sass:editor.css& diff --git a/addons/website/static/src/css/website-flatly.css b/addons/website/static/src/css/website-flatly.css new file mode 100644 index 00000000000..fb50b34062b --- /dev/null +++ b/addons/website/static/src/css/website-flatly.css @@ -0,0 +1,50 @@ +@charset "utf-8"; +/* THIS CSS FILE IS FOR WEBSITE THEMING CUSTOMIZATION ONLY + * + * css for editor buttons, openerp widget included in the website and other + * stuff must go to the editor.css + * + */ +/* ----- GENERIC LAYOUTING HELPERS ---- */ +/* Theming pagination inside navbar */ +.navbar-inner > .pagination { + margin: 6px 0; +} +.navbar-inner > .pagination ul { + position: relative; + top: 2px; + border: solid 2px rgba(0, 0, 0, 0.05); + border-radius: 8px; +} + +/* ---- HOMEPAGE THEME CUSTOMIZATION ---- */ +.subheader { + letter-spacing: 3px; + font-weight: 300; +} + +.dark { + background: #eff8f8; + padding: 64px; +} + +h5 { + font-weight: bold; +} + +#myCarousel .item { + min-height: 300px; + color: white; +} + +.thumbnail { + background: white; +} + +.navbar .nav > li a { + float: none; + padding: 15px 15px 15px; + color: white; + text-decoration: none; + text-shadow: 0 1px 0 #2c3e50; +} diff --git a/addons/website/static/src/css/website-flatly.sass b/addons/website/static/src/css/website-flatly.sass new file mode 100644 index 00000000000..0244d8b7511 --- /dev/null +++ b/addons/website/static/src/css/website-flatly.sass @@ -0,0 +1,49 @@ +@charset "utf-8" + +/* + * THIS CSS FILE IS FOR WEBSITE THEMING CUSTOMIZATION ONLY + * + * css for editor buttons, openerp widget included in the website and other + * stuff must go to the editor.css + * + */ + +/* ----- GENERIC LAYOUTING HELPERS ---- */ + +/* Theming pagination inside navbar */ +.navbar-inner > .pagination + margin: 6px 0 + ul + position: relative + top: 2px + border: solid 2px rgba(0,0,0,0.05) + border-radius: 8px + +/* ---- HOMEPAGE THEME CUSTOMIZATION ---- */ + +.subheader + letter-spacing: 3px + font-weight: 300 + +.dark + background: rgb(239, 248, 248) + padding: 64px + +h5 + font-weight: bold + +#myCarousel .item + min-height: 300px + color: white + +.thumbnail + background: white + +.navbar .nav > li a + float: none + padding: 15px 15px 15px + color: #ffffff + text-decoration: none + text-shadow: 0 1px 0 #2c3e50 + + diff --git a/addons/website/static/src/css/website.css b/addons/website/static/src/css/website.css index f47daad8c83..eea25bea19d 100644 --- a/addons/website/static/src/css/website.css +++ b/addons/website/static/src/css/website.css @@ -1,3 +1,4 @@ +@charset "utf-8"; /* THIS CSS FILE IS FOR WEBSITE THEMING CUSTOMIZATION ONLY * * css for editor buttons, openerp widget included in the website and other @@ -112,17 +113,6 @@ vertical-align: bottom; } -/* Theming pagination inside navbar */ -.navbar-inner > .pagination { - margin: 6px 0; -} -.navbar-inner > .pagination ul { - position: relative; - top: 2px; - border: solid 2px rgba(0, 0, 0, 0.05); - border-radius: 8px; -} - /* ----- BOOTSTRAP HACK FOR STICKY FOOTER ----- */ html, body { height: 100%; @@ -141,42 +131,10 @@ html, body { footer { box-sizing: border-box; - height: 300px; + height: 380px; background: #eff8f8; - padding-top: 64px; - padding-bottom: 64px; -} - -/* ---- HOMEPAGE THEME CUSTOMIZATION ---- */ -.subheader { - letter-spacing: 3px; - font-weight: 300; -} - -header.navbar-fixed-top { - position: static; -} - -.dark { - background: #eff8f8; - padding: 64px; -} - -h5 { - font-weight: bold; -} - -#myCarousel .item { - min-height: 300px; - color: white; -} - -.nav > .pull-right { - float: right !important; -} - -.thumbnail { - background: white; + padding-top: 48px; + padding-bottom: 48px; } /* ---- HACK FOR COVERING UP CK EDITOR BOGUS P INSERTION --- */ @@ -188,14 +146,16 @@ h5 { text-shadow: none; } -.navbar .nav > li a { - float: none; - padding: 15px 15px 15px; - color: white; - text-decoration: none; - text-shadow: 0 1px 0 #2c3e50; -} - .nav > li a { display: block; } + +/* ---- HOMEPAGE THEME CUSTOMIZATION ---- */ +header.navbar-fixed-top { + position: static; +} + +#myCarousel .item { + min-height: 300px; + color: white; +} diff --git a/addons/website/static/src/css/website.sass b/addons/website/static/src/css/website.sass index c5e45aa288f..13037deb2d7 100644 --- a/addons/website/static/src/css/website.sass +++ b/addons/website/static/src/css/website.sass @@ -76,15 +76,6 @@ text-align: left vertical-align: bottom -/* Theming pagination inside navbar */ -.navbar-inner > .pagination - margin: 6px 0 - ul - position: relative - top: 2px - border: solid 2px rgba(0,0,0,0.05) - border-radius: 8px - /* ----- BOOTSTRAP HACK FOR STICKY FOOTER ----- */ html,body @@ -102,37 +93,11 @@ html,body footer box-sizing: border-box - height: 300px + height: 380px background: rgb(239, 248, 248) // rgb(221, 235, 227) - padding-top: 64px - padding-bottom: 64px + padding-top: 48px + padding-bottom: 48px -/* ---- HOMEPAGE THEME CUSTOMIZATION ---- */ - -.subheader - letter-spacing: 3px - font-weight: 300 - -header.navbar-fixed-top - position: static - -.dark - background: rgb(239, 248, 248) - padding: 64px - -h5 - font-weight: bold - -#myCarousel .item - min-height: 300px - color: white - - -.nav > .pull-right - float: right !important //bugfix for flatly theme - -.thumbnail - background: white /* ---- HACK FOR COVERING UP CK EDITOR BOGUS P INSERTION --- */ @@ -142,13 +107,16 @@ h5 .navbar .nav > li a text-shadow: none -.navbar .nav > li a - float: none - padding: 15px 15px 15px - color: #ffffff - text-decoration: none - text-shadow: 0 1px 0 #2c3e50 - .nav > li a display: block +/* ---- HOMEPAGE THEME CUSTOMIZATION ---- */ + +header.navbar-fixed-top + position: static + +#myCarousel .item + min-height: 300px + color: white + + diff --git a/addons/website/static/src/js/website.js b/addons/website/static/src/js/website.js index ba662288aae..2c589a49de5 100644 --- a/addons/website/static/src/js/website.js +++ b/addons/website/static/src/js/website.js @@ -1,20 +1,36 @@ -openerp.website = function(instance) { +// Ugly. I'll clean this monday +$(function () { if ($('html').attr('data-editable') !== '1') return; -instance.web.ActionManager.include({ - // Temporary fix until un-webclientization of the editorbar - ir_actions_client: function (action) { - if (instance.web.client_actions.get_object(action.tag)) { - return this._super.apply(this, arguments); - } else { - console.warn("Action '%s' not found in registry", action.tag); - return $.when(); - } - } -}); +// TODO fme: put everything in openerp.website scope and load templates on +// next tick or document ready +// Also check with xmo if jquery.keypress.js is still needed. -var _lt = instance.web._lt; -var QWeb = instance.web.qweb; -instance.website.EditorBar = instance.web.Widget.extend({ +var doc_ready = $.Deferred(); +$(doc_ready.resolve); + +var templates = [ + '/website/static/src/xml/website.xml' +]; + +function loadTemplates(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(); + } + } + }); + }); + return def; +} + +var EditorBar = openerp.Widget.extend({ template: 'Website.EditorBar', events: { 'click button[data-action=edit]': 'edit', @@ -23,26 +39,55 @@ instance.website.EditorBar = instance.web.Widget.extend({ 'click button[data-action=snippet]': 'snippet', }, container: 'body', - init: function () { - this._super.apply(this, arguments); - this.saving_mutex = new $.Mutex(); + customize_setup: function() { + var self = this; + var view_name = $('html').data('view-xmlid'); + var menu = $('#customize-menu'); + this.$('#customize-menu-button').click(function(event) { + menu.empty(); + openerp.jsonRpc('/website/customize_template_get', 'call', { 'xml_id': view_name }).then( + function(result) { + _.each(result, function (item) { + if (item.header) { + menu.append(''); + } else { + menu.append(_.str.sprintf('
  • %s
  • ', + item.id, item.active ? '' : '-empty', item.name)); + } + }); + } + ); + }); + menu.on('click', 'a', function (event) { + var view_id = $(event.target).data('view-id'); + openerp.jsonRpc('/website/customize_template_toggle', 'call', { + 'view_id': view_id + }).then( function(result) { + window.location.reload(); + }); + }); }, start: function() { var self = this; - this.$('button[data-action]').prop('disabled', true) - .parent().hide(); + this.saving_mutex = new openerp.Mutex(); + + this.$('#website-top-edit').hide(); + this.$('#website-top-view').show(); + + $('.dropdown-toggle').dropdown(); + this.customize_setup(); + this.$buttons = { edit: this.$('button[data-action=edit]'), save: this.$('button[data-action=save]'), cancel: this.$('button[data-action=cancel]'), snippet: this.$('button[data-action=snippet]'), }; - this.$buttons.edit.prop('disabled', false).parent().show(); self.snippet_start(); - this.rte = new instance.website.RTE(this); + this.rte = new RTE(this); this.rte.on('change', this, this.proxy('rte_changed')); return $.when( @@ -51,10 +96,14 @@ instance.website.EditorBar = instance.web.Widget.extend({ ); }, edit: function () { - this.$buttons.edit.prop('disabled', true).parent().hide(); - this.$buttons.cancel.add(this.$buttons.snippet).prop('disabled', false) - .add(this.$buttons.save) - .parent().show(); + this.$buttons.edit.prop('disabled', true); + this.$('#website-top-view').hide(); + this.$('#website-top-edit').show(); + + // 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') @@ -62,7 +111,7 @@ instance.website.EditorBar = instance.web.Widget.extend({ .not('.oe_snippet_editor') .prop('contentEditable', true) .addClass('oe_editable'); - var $rte_ables = $editables.filter('div, p, li, section, header, footer').not('[data-oe-type]'); + var $rte_ables = $editables.not('[data-oe-type]'); var $raw_editables = $editables.not($rte_ables); // temporary fix until we fix ckeditor @@ -121,7 +170,11 @@ instance.website.EditorBar = instance.web.Widget.extend({ .prop('contentEditable', false); html = $w.wrap('
    ').parent().html(); } - return (new instance.web.DataSet(this, 'ir.ui.view')).call('save', [data.oeModel, data.oeId, data.oeField, html, xpath]); + return openerp.jsonRpc('/web/dataset/call', 'call', { + model: 'ir.ui.view', + method: 'save', + args: [data.oeModel, data.oeId, data.oeField, html, xpath] + }); }, cancel: function () { window.location.reload(); @@ -159,7 +212,7 @@ instance.website.EditorBar = instance.web.Widget.extend({ }, }); -instance.website.RTE = instance.web.Widget.extend({ +var RTE = openerp.Widget.extend({ tagName: 'li', id: 'oe_rte_toolbar', className: 'oe_right oe_rte_toolbar', @@ -191,6 +244,9 @@ instance.website.RTE = instance.web.Widget.extend({ 'magicline' ]; return { + // Disable auto-generated titles + // FIXME: accessibility, need to generate user-sensible title, used for @title and @aria-label + title: false, removePlugins: removed_plugins.join(','), uiColor: '', // Ensure no config file is loaded @@ -205,14 +261,22 @@ instance.website.RTE = instance.web.Widget.extend({ // Place toolbar in controlled location sharedSpaces: { top: 'oe_rte_toolbar' }, toolbar: [ - {name: 'items', items: [ + {name: 'basicstyles', items: [ "Bold", "Italic", "Underline", "Strike", "Subscript", - "Superscript", "TextColor", "BGColor", "RemoveFormat", + "Superscript", "TextColor", "BGColor", "RemoveFormat" + ]},{ + name: 'span', items: [ "Link", "Unlink", "Blockquote", "BulletedList", - "NumberedList", "Image", "Indent", "Outdent", - "JustifyLeft", "JustifyCenter", "JustifyRight", - "JustifyBlock", "Table", "Font", "FontSize", "Format", - "Styles" + "NumberedList", "Indent", "Outdent", + ]},{ + name: 'justify', items: [ + "JustifyLeft", "JustifyCenter", "JustifyRight", "JustifyBlock" + ]},{ + name: 'special', items: [ + "Image", "Table" + ]},{ + name: 'styles', items: [ + "Format", "Styles" ]} ], // styles dropdown in toolbar @@ -232,7 +296,7 @@ instance.website.RTE = instance.web.Widget.extend({ var self = this; $('.carousel .js_carousel_options .label').on('click', function (e) { e.preventDefault(); - var $button = $(e.currentTarget) + var $button = $(e.currentTarget); var $c = $button.parents(".carousel:first"); if($button.hasClass("js_add")) { @@ -251,7 +315,12 @@ instance.website.RTE = instance.web.Widget.extend({ } }); -$(function(){ +$.when(doc_ready, loadTemplates(templates)).then(function() { + var editor = new EditorBar(); + editor.prependTo($('body')); + $('body').css('padding-top', '50px'); // Not working properly: editor.$el.outerHeight()); + + // TODO: Create an openerp.Widget out of this function make_static(){ $('.oe_snippet_demo').removeClass('oe_new'); @@ -307,6 +376,12 @@ $(function(){ }); } + function customier_option_get(event){ + + + event.preventDefault(); + } + function append_snippet(event){ console.log('click',this,event.button); if(event.button === 0){ @@ -330,17 +405,26 @@ $(function(){ } }); -instance.web.ActionRedirect = function(parent, action) { - var url = $.deparam(window.location.href).url; - if (url) { - window.location.href = url; - } -}; -instance.web.client_actions.add("redirect", "instance.web.ActionRedirect"); +}); +// }()); I'll clean this monday -instance.web.GoToWebsite = function(parent, action) { - window.location.href = window.location.href.replace(/[?#].*/, '').replace(/\/admin[\/]?$/, ''); -}; -instance.web.client_actions.add("website.gotowebsite", "instance.web.GoToWebsite"); -}; +$(function () { + $(document).on('click', '.js_publish, .js_unpublish', function (e) { + e.preventDefault(); + var $link = $(this).parent(); + $link.find('.js_publish, .js_unpublish').addClass("hidden"); + var $unp = $link.find(".js_unpublish"); + var $p = $link.find(".js_publish"); + $.post('/website/publish', {'id': $link.data('id'), 'object': $link.data('object')}, function (result) { + if (+result) { + $p.addClass("hidden"); + $unp.removeClass("hidden"); + } else { + $p.removeClass("hidden"); + $unp.addClass("hidden"); + } + }); + }); +}); + diff --git a/addons/website/static/src/js/website_bootstrap.js b/addons/website/static/src/js/website_bootstrap.js deleted file mode 100644 index 68d2388be68..00000000000 --- a/addons/website/static/src/js/website_bootstrap.js +++ /dev/null @@ -1,19 +0,0 @@ -$(function() { - // Init headless webclient - // TODO: Webclient research : use iframe embedding mode - // Meanwhile, let's HACK !!! - var s = new openerp.init(['web', 'website']); - s.web.WebClient.bind_hashchange = s.web.WebClient.show_common = s.web.blockUI = s.web.unblockUI = function() {}; - s.web.WebClient.include({ do_push_state: function() {} }); - var wc = new s.web.WebClient(); - wc.start(); - var instance = openerp.instances[wc.session.name]; - // Another hack since we have no callback when webclient has loaded modules. - instance.web.qweb.add_template('/website/static/src/xml/website.xml'); - - $(function() { - var editor = new instance.website.EditorBar(instance.webclient); - editor.prependTo($('body')); - $('body').css('padding-top', '32px'); // Not working properly: editor.$el.outerHeight()); - }); -}); diff --git a/addons/website/static/src/xml/website.xml b/addons/website/static/src/xml/website.xml index 941015fd0f5..9f11ff40bca 100644 --- a/addons/website/static/src/xml/website.xml +++ b/addons/website/static/src/xml/website.xml @@ -3,12 +3,43 @@ --> -
      -
    • -
    • -
    • -
    • -
    + + +