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

bzr revid: bth@tinyerp.com-20130925093523-xq6otgmj3xdwrunr
This commit is contained in:
bth-openerp 2013-09-25 15:05:23 +05:30
commit 55410d9a4d
45 changed files with 1214 additions and 514 deletions

View File

@ -51,7 +51,9 @@ Key Features
'res_partner_view.xml',
'email_template.xml',
],
'demo': ['event_demo.xml'],
'demo': [
'event_demo.xml',
],
'test': ['test/process/event_draft2done.yml'],
'css': ['static/src/css/event.css'],
'installable': True,

View File

@ -212,7 +212,10 @@ class event_event(osv.osv):
'speaker_confirmed': fields.boolean('Speaker Confirmed', readonly=False, states={'done': [('readonly', True)]}),
'country_id': fields.related('address_id', 'country_id',
type='many2one', relation='res.country', string='Country', readonly=False, states={'done': [('readonly', True)]}, store=True),
'note': fields.text('Description', readonly=False, states={'done': [('readonly', True)]}),
'description': fields.html(
'Description', readonly=False,
states={'done': [('readonly', True)]},
oldname='note'),
'company_id': fields.many2one('res.company', 'Company', required=False, change_default=True, readonly=False, states={'done': [('readonly', True)]}),
'is_subscribed' : fields.function(_subscribe_fnc, type="boolean", string='Subscribed'),
'visibility': fields.selection(_visibility_selection, 'Privacy / Visibility',

View File

@ -27,31 +27,185 @@
<field name="name">Training</field>
</record>
<!-- Demo data for Event -->
<!-- event.event -->
<record id="event_0" model="event.event">
<field name="name">Concert of Bon Jovi</field>
<field eval="time.strftime('%Y-%m-01 19:05:15')" name="date_begin"/>
<field eval="time.strftime('%Y-%m-01 23:05:15')" name="date_end"/>
<field name="name">Open Days in Los Angeles</field>
<field eval="(DateTime.now() + timedelta(days=1)).strftime('%Y-%m-%d 8:00:00')" name="date_begin"/>
<field eval="(DateTime.now() + timedelta(days=5)).strftime('%Y-%m-%d 18:00:00')" name="date_end"/>
<field name="register_max">500</field>
<field name="address_id" ref="base.res_partner_6"/>
<field name="type" ref="event_type_1"/>
<field name="description"><![CDATA[
<div class="oe_structure">
<center><strong>Join us to our main event of the year: the Open Days</strong></center>
<p>&nbsp;</p>
<p>Every year we invite our community, partners and end-users to come and meet us! It's the ideal event to get together and present new features, roadmap of future versions, achievements of the software, workshops, training sessions, etc.... This event is also an opportunity to showcase our partners' case studies, methodology or developments. Be there and see directly from the source the features of the version 8!
</p>
<p>This event and all the conferences are in english!</p>
<p>&nbsp;</p>
<p><strong>What's new for this year?</strong></p>
<ul>
<li>The Open Days are preceded by 2 days of optional training sessions for experts! We propose 3 different training sessions, 2 days each.</li>
<li>The whole event is open to all public! We ask a participation fee of 49.50€ for the costs for the 3 days (morning coffee, coffee breaks, drinks, sandwiches for lunch and the surprising beer party of Wednesday evening) but it's optional. For those who do not want to contribute, there is a free ticket, therefore, catering is not inclued.</li>
<li>The plenary sessions in the morning will be shorter and we will give more time for thematical meetings, conferences, workshops and tutorial sessions in the afternoon.</li>
</ul>
<p>&nbsp;</p>
<p><strong>Program:</strong></p>
<p>Conferences, workshops and trainings will be organized in 6 rooms:</p>
<ul>
<li>2 technical rooms: one dedicated to advanced OpenERP developers, one for new developers.</li>
<li>2 technical rooms: one dedicated to advanced OpenERP developers, one for new developers.</li>
<li>1 business room: to discuss implementation methodologies, best sales practices, etc.</li>
<li>1 workshop room: mainly for developers.</li>
</ul>
<p><em>If you wish to make a presentation, please send your topic proposal as soon as possible for approval to Mr. Famke Jenssens at ngh (a) yourcompany (dot) com. The presentations should be, for example, a presentation of a community module, a case study, methodology feedback, technical, etc. Each presentation must be in English.</em></p>
<p>&nbsp;</p>
<p><strong>Where to find us:</strong></p>
<p>OpenElec Applications 23 Rockwell Lane, Los Angeles, CA 90001, United States</p>
<p>For any additional information, please contact us at <a href="mailto:events@yourcompany.com">events@yourcompany.com</a>.</p>
<p>&nbsp;</p>
<p>Best regards,</p>
<p>Luigi Roni, Senior Event Manager</p>
<p>&nbsp;</p>
<p align="RIGHT"><em>(OpenElec Applications reserves the right to cancel, re-name or re-locate<br/>the event or change the dates on which it is held.)</em></p>
</div>
]]></field>
</record>
<record id="event_1" model="event.event">
<field name="name">Opera of Verdi</field>
<field eval="(DateTime.today()+ timedelta(days=1)).strftime('%Y-%m-%d 18:00:00')" name="date_begin"/>
<field eval="(DateTime.today()+ timedelta(days=2)).strftime('%Y-%m-%d 21:00:00')" name="date_end"/>
<field name="type" ref="event_type_1"/>
<field name="register_min">50</field>
<field name="name">Functional Webinar</field>
<field eval="(DateTime.now() + timedelta(days=3)).strftime('%Y-%m-%d 7:00:00')" name="date_begin"/>
<field eval="(DateTime.now() + timedelta(days=3)).strftime('%Y-%m-%d 11:00:00')" name="date_end"/>
<field name="type" ref="event_type_0"/>
<field name="address_id" ref="base.res_partner_5"/>
<field name="register_max">350</field>
<field name="description"><![CDATA[
<div class="oe_structure">
<center><strong>Functional Webinar</strong></center>
<p>&nbsp;</p>
<p>Webinars are online demonstrations where one of our team members explains the main features and benefits of our online offer through an online conference. We can therefore directly answer any questions you may have through a Q&A.</p>
<p>Each session lasts approximately one hour and is free, we just ask you to register to receive access codes.</p>
<p>&nbsp;</p>
<p><strong>Objective:</strong></p>
<p>These webinars allow companies interested in our software, to assess whether the solution meets their needs, and can adapt to the scope of their project.</p>
<p>This webinar helps participants to:
<ul>
<li>Discover how to navigate in our software;</li>
<li>View full flow: purchasing, sales, project management, accounting;</li>
<li>Ask your questions to our expert;</li>
<li>Assess whether your expectations are met;</li>
</ul>
<p>&nbsp;</p>
<p>After registering, you will receive a link and password by email before the start of the session. If you have a problem to connect, please contact us at <a href="mailto:events@yourcompany.com">events@yourcompany.com</a>.</p>
<p>&nbsp;</p>
<p>We are looking forward to meeting you online,</p>
<p>Best regards,</p>
<p>Luigi Roni, Senior Event Manager</p>
<p>&nbsp;</p>
<p align="RIGHT"><em>(YourCompany reserves the right to cancel, re-name or re-locate<br/>the event or change the dates on which it is held.)</em></p>
</div>
]]></field>
</record>
<record id="event_2" model="event.event">
<field name="name">Conference on ERP Business</field>
<field eval="(DateTime.today()+ timedelta(days=2)).strftime('%Y-%m-%d 14:00:00')" name="date_begin"/>
<field eval="(DateTime.today()+ timedelta(days=2)).strftime('%Y-%m-%d 16:30:00')" name="date_end"/>
<field name="name">Conference on Business Applications</field>
<field eval="(DateTime.today()+ timedelta(days=5)).strftime('%Y-%m-%d 7:00:00')" name="date_begin"/>
<field eval="(DateTime.today()+ timedelta(days=5)).strftime('%Y-%m-%d 16:30:00')" name="date_end"/>
<field name="type" ref="event_type_2"/>
<field name="address_id" ref="base.res_partner_14"/>
<field name="register_max">200</field>
<field name="description"><![CDATA[
<div class="oe_structure">
<center><strong>Conference on Business Applications</strong></center>
<p>&nbsp;</p>
<p> During this conference, our team will give a detailed overview of our business applications. Youll know all the benefits of using it.</p>
<p>&nbsp;</p>
<p><strong>Objectives:</strong></p>
<p>Having attended this conference, participants should be able to:</p>
<ul>
<li>Understand the various modules;</li>
<li>Functional flow of the main applications;</li>
</ul>
<p>&nbsp;</p>
<p><strong>Program:</strong></p>
<ul>
<li>Introduction, CRM, Sales Management</li>
<li>Purchase, Sales & Purchase management, Financial accounting.</li>
<li>Project management, Human resources, Contract management.</li>
<li>Warehouse management, Manufacturing (MRP) & Sales, Import/Export.</li>
<li>Point of Sale (POS), Introduction to report customization.</li>
</ul>
<p>&nbsp;</p>
<p><strong>Where to find us:</strong></p>
<p>Chamber Works 60, Rosewood Court Detroit, MI 48212 (United States)</p>
<p>For any additional information, please contact us at <a href="mailto:events@openerp.com">events@openerp.com</a>.</p>
<p>&nbsp;</p>
<p>Best regards,</p>
<p>Luigi Roni, Senior Event Manager</p>
<p>&nbsp;</p>
<p align="RIGHT"><em>(Chamber Works reserves the right to cancel, re-name or re-locate<br/>the event or change the dates on which it is held.)</em></p>
</div>
]]></field>
</record>
<function model="event.event" name="button_confirm" eval="[ref('event_2')]"/>
<record id="event_3" model="event.event">
<field name="name">Technical Training</field>
<field eval="(DateTime.now() + timedelta(15)).strftime('%Y-%m-%d 7:00:00')" name="date_begin"/>
<field eval="(DateTime.now() + timedelta(20)).strftime('%Y-%m-%d 16:00:00')" name="date_end"/>
<field name="type" ref="event_type_4"/>
<field name="user_id" ref="base.user_root"/>
<field name="address_id" ref="base.res_partner_2"/>
<field name="organizer_id" ref="base.res_partner_address_4"/>
<field name="description"><![CDATA[
<div class="oe_structure">
<center><strong>5-days Technical Training</strong></center>
<p>&nbsp;</p>
<p><strong>Course summary:</strong></p>
<p>This course is dedicated to partners, integrators and developers who need to grasp knowledge of the business applications development process. This course is for new developers or for IT professionals eager to learn more about technical aspects.</p>
<p>&nbsp;</p>
<p><strong>Objectives:</strong></p>
<p>Having attended this course, participants should be able to:</p>
<ul>
<li>Understand the development concepts and architecture;</li>
<li>Install and administer your own server;</li>
<li>Develop a new module for a particular application.</li>
</ul>
<p>&nbsp;</p>
<p><strong>Our prices include:</strong></p>
<ul>
<li>drinks and lunch;</li>
<li>training material.</li>
</ul>
<p>&nbsp;</p>
<p><strong>Requirements:</strong></p>
<ul>
<li>Bring your own laptop.</li>
<li>Participants are expected to have some knowledge in programming.&nbsp;A basic knowledge of the Python programming is recommended.</li>
<li>Participants preferably have a functional knowledge of our software (see Functional Training).</li>
</ul>
<p>&nbsp;</p>
<p>For more information on the program, please explore <a href="http://www.openerp.com/services/technical-training">http://www.openerp.com/services/technical-training</a>.</p>
<p>If you have a question<strong>&nbsp;concerning the content of the training</strong>, please contact <a href="mailto:events@yourcompany.com">events@yourcompany.com</a>.</p>
<p>&nbsp;</p>
<p><strong>Where to find us:</strong></p>
<p>Chaussée de Namur 69, 1300 Wavre, Belgium</p>
<p>More information about our Headquarter office (directions, transports, parking, hotels, ...), please have a look at&nbsp;<a href="http://bit.ly/VD8J67">http://bit.ly/VD8J67.</a></p>
<p>&nbsp;</p>
<p><strong>Cancellation Policy:&nbsp;</strong></p>
<p>The organization of the training session has related costs. Due to these costs, cancellations made less than 2 weeks (14 calendar days) prior to the start of the training session is a subject to a fee. This fee can be up to a maximum of 1000€ per cancellation request.</p>
<p>We strongly recommend to book your flight tickets and/or hotel reservations 2 weeks prior to the training. If the training is cancelled 2 weeks in advance, you'll be notified by email.</p>
<div>&nbsp;</div>
<p>For any additional information, please contact us at <a href="mailto:events@openerp.com">events@openerp.com</a>.</p>
<p>&nbsp;</p>
<p>Best regards,</p>
<p>Luigi Roni, Senior Event Manager</p>
<p>&nbsp;</p>
<p align="RIGHT"><em>(YourCompany reserves the right to cancel, re-name or re-locate<br/>the event or change the dates on which it is held.)</em></p>
</div>
]]></field>
</record>
<function model="event.event" name="button_confirm" eval="[ref('event_0')]"/>
<function model="event.event" name="button_confirm" eval="[ref('event_1')]"/>
<function model="event.event" name="button_confirm" eval="[ref('event_2')]"/>
<!-- Demo data for Event Registration-->

View File

@ -91,14 +91,6 @@
</group>
</group>
<notebook>
<page string="Event Details" groups="base.group_no_one">
<group colspan="4">
<field name="reply_to"/>
<field name="email_registration_id"/>
<field name="email_confirmation_id"/>
</group>
<field name="note" nolabel="1" placeholder="Event Description..."/>
</page>
<page string="Registrations">
<group>
<group>
@ -149,6 +141,16 @@
</form>
</field>
</page>
<page string="Description">
<field name="description" nolabel="1" placeholder="Event Description..."/>
</page>
<page string='Event Details' groups="base.group_no_one">
<group colspan="4">
<field name="reply_to"/>
<field name="email_registration_id"/>
<field name="email_confirmation_id"/>
</group>
</page>
</notebook>
</sheet>
<div class="oe_chatter">

File diff suppressed because one or more lines are too long

View File

@ -117,10 +117,8 @@ class event_event(osv.osv):
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"),
'organizer_id': fields.many2one('res.partner', "Organizer"),
'phone': fields.related('organizer_id', 'phone', type='char', string='Phone'),
'email': fields.related('organizer_id', 'email', type='char', string='Email'),
'register_max': fields.function(_get_register_max,

View File

@ -245,7 +245,7 @@ FaceTime HD Camera, 1.2 MP Photos</field>
<field name="name">Bose Mini Bluetooth Speaker</field>
<field name="default_code">PC-DEM</field>
<field name="categ_id" ref="product_category_4"/>
<field name="public_categ_id" ref="services"/>
<field name="public_categ_id" ref="Speakers"/>
<field name="standard_price">600.0</field>
<field name="list_price">900.0</field>
<field name="type">consu</field>
@ -311,7 +311,8 @@ FaceTime HD Camera, 1.2 MP Photos</field>
<field name="uom_id" ref="product_uom_unit"/>
<field name="uom_po_id" ref="product_uom_unit"/>
</record>
<record id="product_product_11" model="product.product">
<record id="product_product_11_temp" model="product.template">
<field name="name">iPod</field>
<field name="default_code">M-Las</field>
<field name="categ_id" ref="product_category_8"/>
@ -322,6 +323,24 @@ FaceTime HD Camera, 1.2 MP Photos</field>
<field name="uom_id" ref="product_uom_unit"/>
<field name="uom_po_id" ref="product_uom_unit"/>
</record>
<record id="product_product_11" model="product.product">
<field name="name">iPod</field>
<field name="default_code">M-Las</field>
<field name="categ_id" ref="product_category_8"/>
<field name="public_categ_id" ref="Keyboard_Mouse"/>
<field name="standard_price">14</field>
<field name="list_price">16.50</field>
<field name="type">consu</field>
<field name="uom_id" ref="product_uom_unit"/>
<field name="uom_po_id" ref="product_uom_unit"/>
<field name="product_tmpl_id" ref="product_product_11_temp"/>
</record>
<record id="product_product_11_b" model="product.product">
<field name="variants">32 Gb</field>
<field name="price_extra">12</field>
<field name="product_tmpl_id" ref="product_product_11_temp"/>
</record>
<record id="product_product_12" model="product.product">
<field name="name">Mouse, Wireless</field>
<field name="default_code">M-Wir</field>
@ -1023,12 +1042,12 @@ QWERTY keyboard</field>
<field name="min_qty">1</field>
</record>
<record id="product_supplierinfo_38" model="product.supplierinfo">
<!--record id="product_supplierinfo_38" model="product.supplierinfo">
<field name="product_id" ref="product_product_48"/>
<field name="name" ref="base.res_partner_8"/>
<field name="delay">7</field>
<field name="min_qty">1</field>
</record>
</record-->
<record id="product_supplierinfo_39" model="product.supplierinfo">
<field name="product_id" ref="product_product_18"/>

File diff suppressed because one or more lines are too long

View File

@ -220,12 +220,16 @@ class Website(openerp.addons.web.controllers.main.Home):
hashed_session = hashlib.md5(request.session_id).hexdigest()
retag = hashed_session
try:
ids = Model.read(request.cr, request.uid, [('id', '=', id)], request.context)
if not ids:
id = Model.read(request.cr, openerp.SUPERUSER_ID, [('id', '=', id), ('website_published', '=', True)], request.context)[0]
if etag:
date = Model.read(request.cr, request.uid, [id], [last_update], request.context)[0].get(last_update)
date = Model.read(request.cr, openerp.SUPERUSER_ID, [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]
res = Model.read(request.cr, openerp.SUPERUSER_ID, [id], [last_update, field], request.context)[0]
retag = hashlib.md5(res.get(last_update)).hexdigest()
image_base64 = res.get(field)

View File

@ -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
@ -228,7 +229,7 @@ footer {
}
.oe_structure.oe_empty:empty:before, [data-oe-type=html]:empty:before, .oe_structure.oe_empty > .oe_drop_zone.oe_insert:only-child:before, [data-oe-type=html] > .oe_drop_zone.oe_insert:only-child:before {
content: "Drag Building Blocks Here";
content: "Click Edit To Create Content";
text-align: center;
display: block;
padding-top: 100px;
@ -237,6 +238,10 @@ footer {
font-size: 24px;
}
.oe_structure.oe_editable.oe_empty:empty:before, .oe_editable[data-oe-type=html]:empty:before, .oe_structure.oe_editable.oe_empty > .oe_drop_zone.oe_insert:only-child:before, [data-oe-type=html] > .oe_drop_zone.oe_insert:only-child:before {
content: "Drag Building Blocks Here";
}
/* ---- HACK FOR COVERING UP CK EDITOR BOGUS P INSERTION --- */
.navbar .nav > li > p {
margin-bottom: 0px;
@ -370,17 +375,21 @@ a[data-publish][data-publish='on']:hover .css_published {
display: none;
}
[data-publish='off']:not(a) > :not([data-publish]) {
.unpublish {
opacity: 0.5;
}
[data-publish='off']:not(a) > :not(.js_publish) {
opacity: 0.5;
}
[data-publish]:not(a) {
position: relative;
overflow: visible;
/*&:hover > [data-publish] */
/*&:hover .js_publish */
/* display: block */
}
[data-publish]:not(a) > [data-publish] {
[data-publish]:not(a) .js_publish {
position: absolute;
right: -6px;
top: -10px;

View File

@ -155,7 +155,7 @@ footer
position: static
.oe_structure.oe_empty:empty:before, [data-oe-type=html]:empty:before, .oe_structure.oe_empty > .oe_drop_zone.oe_insert:only-child:before, [data-oe-type=html] > .oe_drop_zone.oe_insert:only-child:before
content: 'Drag Building Blocks Here'
content: 'Click Edit To Create Content'
text-align: center
display: block
padding-top: 100px
@ -163,6 +163,9 @@ footer
color: grey
font-size: 24px
.oe_structure.oe_editable.oe_empty:empty:before, .oe_editable[data-oe-type=html]:empty:before, .oe_structure.oe_editable.oe_empty > .oe_drop_zone.oe_insert:only-child:before, [data-oe-type=html] > .oe_drop_zone.oe_insert:only-child:before
content: 'Drag Building Blocks Here'
/* ---- HACK FOR COVERING UP CK EDITOR BOGUS P INSERTION --- */
.navbar .nav > li > p
@ -282,16 +285,18 @@ a[data-publish]
&:hover .css_published
display: none
.unpublish
opacity: 0.5
[data-publish='off']:not(a)
>:not([data-publish])
>:not(.js_publish)
opacity: 0.5
[data-publish]:not(a)
position: relative
overflow: visible
>[data-publish]
.js_publish
position: absolute
right: -6px
top: -10px
display: none
/*&:hover > [data-publish]*/
/*&:hover .js_publish*/
/* display: block*/

View File

@ -321,16 +321,16 @@
var self = this;
// create a single editor for the whole page
var root = document.getElementById('wrapwrap');
$(root).attr('data-cke-editable', 'true')
.on('dragstart', 'img', function (e) {
e.preventDefault();
});
this.editor = CKEDITOR.inline(root, self._config());
this.editor.on('instanceReady', function () {
$(root).on('dragstart', 'img', function (e) {
e.preventDefault();
});
var editor = this.editor = CKEDITOR.inline(root, self._config());
editor.on('instanceReady', function () {
editor.setReadOnly(false);
// ckeditor set root to editable, disable it (only inner
// sections are editable)
// FIXME: are there cases where the whole editor is editable?
root.contentEditable = false;
editor.editable().setReadOnly(true);
self.setup_editables(root);
@ -496,11 +496,15 @@
this.changed($target.find('.url-source'));
},
'click button.remove': 'remove_link',
'change input#link-text': function (e) {
this.text = $(e.target).val()
},
}),
init: function (editor) {
this._super(editor);
// url -> name mapping for existing pages
this.pages = Object.create(null);
this.text = null;
},
start: function () {
var element;
@ -554,13 +558,15 @@
if (this.element) {
this.element.setAttributes(attributes);
this.element.removeAttributes(to_remove);
if (this.text) { this.element.setText(this.text); }
} else {
var selection = this.editor.getSelection();
var range = selection.getRanges(true)[0];
if (range.collapsed) {
//noinspection JSPotentiallyInvalidConstructorUsage
var text = new CKEDITOR.dom.text(label || url);
var text = new CKEDITOR.dom.text(
this.text || label || url);
range.insertNode(text);
range.selectNodeContents(text);
}
@ -628,6 +634,7 @@
this.changed($control);
this.$('input#link-text').val(this.element.getText());
this.$('input.window-new').prop(
'checked', this.element.getAttribute('target') === '_blank');
},

View File

@ -116,20 +116,18 @@
dom_ready.then(function () {
/* ----- PUBLISHING STUFF ---- */
$('[data-publish]:has([data-publish])').each(function () {
var $pub = $("[data-publish]", this);
if($pub.size())
$(this).attr("data-publish", $pub.attr("data-publish"));
else
$(this).removeAttr("data-publish");
$('[data-publish]:has(.js_publish)').each(function () {
$(this).attr("data-publish", $(".js_publish li.active", this).size() ? "on" : 'off');
});
$(document).on('click', '.js_publish', function (e) {
e.preventDefault();
var $data = $(":first", this).parents("[data-publish]");
$data.attr("data-publish", $data.first().attr("data-publish") == 'off' ? 'on' : 'off');
$.post('/website/publish', {'id': $(this).data('id'), 'object': $(this).data('object')}, function (result) {
$data.attr("data-publish", +result ? 'on' : 'off');
$(document).on('click', '.js_publish a.js_publish_btn', function (e) {
var $li = $(this).parent("li");
var $data = $li.parents(".js_publish:first");
var publish = $li.hasClass("active");
$li.toggleClass("active");
$.post('/website/publish', {'id': $data.data('id'), 'object': $data.data('object')}, function (result) {
$li.toggleClass("active", !!+result);
$li.parents("[data-publish]").attr("data-publish", +result ? 'on' : 'off');
});
});

View File

@ -1027,7 +1027,6 @@
this.set_options_background();
this.set_options_style();
}
console.log(nb);
if (nb <= 1) {
this.$target.find('.carousel-control').addClass("hidden");
}
@ -1159,7 +1158,12 @@
}
});
website.snippet.selector.push([ _.map([1,2,3,4,5,6,7,8,9,10,11,12], function (v) {return '.row > .col-md-'+v;}).join(","), 'colmd']);
/*
* data-snippet-id automatically setted
* Don't need to add data-snippet-id="..." into the views
*/
website.snippet.selector.push([".row > div[class*='col-md-']", 'colmd']);
website.snippet.selector.push(['hr', 'hr']);
})();

View File

@ -83,6 +83,17 @@
id="link-email" placeholder="you@yourwebsite.com"/>
</li>
</ul>
<div class="form-horizontal">
<div class="form-group">
<label for="link-text" class="col-sm-2 control-label">
Link text
</label>
<div class="col-sm-10">
<input type="text" class="form-control"
id="link-text"/>
</div>
</div>
</div>
</form>
</t>
</t>

View File

@ -336,12 +336,15 @@
</template>
<template id="publish">
<a href="#" t-att-data-id="object.id" t-att-data-object="object._name" t-att-data-publish="object.id and object.website_published and 'on' or 'off'" class="pull-right js_publish" t-if="editable" t-ignore="true">
<span t-attf-class="label label-success css_publish">Publish</span>
<span t-attf-class="label label-danger css_unpublish">Unpublish</span>
<span t-attf-class="label label-success css_published">Published</span>
<span t-attf-class="label label-danger css_unpublished">Unpublished</span>
</a>
<t t-if="editable" t-ignore="true">
<div class="dropdown js_publish pull-right" t-att-data-id="object.id" t-att-data-object="object._name">
<a class="btn btn-default" id="dopprod" role="button" data-toggle="dropdown"> Manage <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu" aria-labelledby="dopprod">
<li t-att-class="object.id and object.website_published and 'active' or ''"><a href="#" class="js_publish_btn">Publish</a></li>
<li><a t-att-href="'/admin/#model=%s&amp;id=%s' % (object._name, object.id)">Manage Products</a></li>
</ul>
</div>
</t>
</template>
<template id="kanban">

View File

@ -95,10 +95,10 @@ class website(osv.osv):
qweb_context = request.context.copy()
if values is None:
values = {}
if values:
qweb_context.update(values)
values.update(
qweb_context.update(
request=request,
registry=request.registry,
json=simplejson,
@ -107,7 +107,6 @@ class website(osv.osv):
user_id=user.browse(cr, openerp.SUPERUSER_ID, uid),
)
qweb_context.update(values)
context = {
'inherit_branding': qweb_context.setdefault('editable', False),
}

View File

@ -1,2 +1,2 @@
sass:
sass --compass --trace -t expanded website_blog.sass website_blog.css
sass --trace -t expanded website_blog.sass:website_blog.css

View File

@ -1,3 +1,4 @@
@import url(compass/css3.css);
.css_website_mail .has-error {
border-color: red;
}
@ -7,3 +8,16 @@
.css_website_mail .css_nav_month:first-of-type {
display: block;
}
.blog_content a.oe_mail_expand:after {
content: " →";
}
.blog_content a.oe_mail_expand {
font-weight: bold;
display: block;
}
p.post-meta {
position: relative;
top: -5px;
}

View File

@ -7,3 +7,15 @@
display: none
&:first-of-type
display: block
.blog_content
a.oe_mail_expand:after
content: ""
a.oe_mail_expand
font-weight: bold
display: block
p.post-meta
position: relative
top: -5px

View File

@ -14,70 +14,80 @@
<!-- Blog Post Summary -->
<template id="view_blog_post_short" name="Blog Post Short">
<div class="media-body">
<t t-call="website_mail.follow"><t t-set="object" t-value="blog_post"/></t>
<t t-call="website.publish"><t t-set="object" t-value="blog_post"/></t>
<small class="text-muted">
<t t-field="blog_post.create_uid"/> on <t t-field="blog_post.create_date"/>
</small>
<h4 class="media-heading" ><a t-attf-href="/blog/#{blog_post.category_id.id}/#{blog_post.id}#comment" t-field="blog_post.name"></a></h4>
<div class="media">
<div t-field="blog_post.shortened_content"/>
<small class="pull-left muted text-right">
<a t-if="len(blog_post.message_ids) &lt;= 1" t-attf-href="/blog/#{blog_post.category_id.id}/#{blog_post.id}#comments"><t t-esc="len(blog_post.message_ids)"/> Comment</a>
<a t-if="len(blog_post.message_ids) > 1" t-attf-href="/blog/#{blog_post.category_id.id}/#{blog_post.id}#comments"><t t-esc="len(blog_post.message_ids)"/> Comments</a>
</small>
</div>
<div>
<h2 class="text-center">
<a t-attf-href="/blog/#{blog_post.category_id.id}/#{blog_post.id}#comment" t-field="blog_post.name"></a>
</h2>
<p class="post-meta text-muted text-center">
<span class="icon-calendar"> <t t-field="blog_post.create_date"/></span> &amp;nbsp;
<span class="icon-user"> By <t t-field="blog_post.create_uid"/></span> &amp;nbsp;
<span t-if="len(blog_post.message_ids) &gt; 0" class="icon-comment"> With
<a t-if="len(blog_post.message_ids) &lt;= 1" t-attf-href="/blog/#{blog_post.category_id.id}/#{blog_post.id}#comments"><t t-esc="len(blog_post.message_ids)"/> comment</a>
<a t-if="len(blog_post.message_ids) > 1" t-attf-href="/blog/#{blog_post.category_id.id}/#{blog_post.id}#comments"><t t-esc="len(blog_post.message_ids)"/> comments</a>
</span>
</p>
<div t-field="blog_post.shortened_content" class="blog_content"/>
<hr/>
</div>
</template>
<!-- Blog Post Complete -->
<template id="view_blog_post" name="Blog Post">
<div class="media">
<div class="media-body">
<t t-call="website_mail.follow"><t t-set="object" t-value="blog_post"/></t>
<t t-call="website.publish"><t t-set="object" t-value="blog_post"/></t>
<small class="text-muted">
<t t-field="blog_post.create_uid"/> on <t t-field="blog_post.create_date"/>
</small>
<h3 t-field="blog_post.name"/>
<t t-foreach="blog_post.tag_ids" t-as="tag">
<a class="btn btn-sm btn-info" href="/blog/tag/#{tag.id}"><t t-esc="tag.name"/></a>
</t>
<div t-field="blog_post.content"/>
<div>
<t t-call="website_mail.follow"><t t-set="object" t-value="blog_post"/></t>
<t t-call="website.publish"><t t-set="object" t-value="blog_post"/></t>
</div><div class="clearfix"/>
<h2 class="text-center" t-field="blog_post.name"/>
<p class="post-meta text-muted text-center">
<span class="icon-calendar"> <t t-field="blog_post.create_date"/></span> &amp;nbsp;
<span class="icon-user"> By <t t-field="blog_post.create_uid"/></span> &amp;nbsp;
<span t-if="len(blog_post.message_ids) &gt; 0" class="icon-comment"> With
<a t-if="len(blog_post.message_ids) &lt;= 1" t-attf-href="#comments"><t t-esc="len(blog_post.message_ids)"/> comment</a>
<a t-if="len(blog_post.message_ids) > 1" t-attf-href="#comments"><t t-esc="len(blog_post.message_ids)"/> comments</a>
</span>
</p>
<p class="post-meta text-muted text-center" t-if="len(blog_post.tag_ids)">
<span class="icon-tags"/>
<t t-foreach="blog_post.tag_ids" t-as="tag">
<a href="/blog/tag/#{tag.id}" t-esc="tag.name"/> &amp;nbsp;
</t>
</p>
<div t-field="blog_post.content" class="mt32"/>
<hr/>
<div class="clearfix">
<div class="pull-right text-right">
<t t-call="website.pager" class="pull-right"/>
</div>
<hr />
<div class="clearfix">
<div class="pull-right text-right">
<t t-call="website.pager" class="pull-right"/>
</div>
<t t-if="len(blog_post.website_message_ids) &lt;= 1">
<t t-esc="len(blog_post.website_message_ids)"/> Comment
</t>
<t t-if="len(blog_post.website_message_ids) > 1">
<t t-esc="len(blog_post.website_message_ids)"/> Comments
</t>
</div>
<form id="comment" t-attf-action="/blog/#{blog_post.category_id.id}/#{blog_post.id}/post#post"
method="POST" class="form-horizontal text-center"
groups="group_website_blog_reply">
<div>
<textarea rows="4" placeholder="Your comment" class="form-control"></textarea>
</div>
<button type="submit" class="btn btn-default">Post your comment</button>
</form>
<ul class="media-list" id="comments">
<li t-foreach="blog_post.website_message_ids" t-as="message" class="media">
<div class="media-body well well-sm">
<t t-call="website.publish"><t t-set="object" t-value="message"/></t>
<t t-raw="message.body"/>
<small class="pull-left text-muted text-left">
<t t-field="message.author_id"/> on <t t-field="message.date"/>
</small>
</div>
</li>
</ul>
<t t-if="len(blog_post.website_message_ids) &lt;= 1">
<t t-esc="len(blog_post.website_message_ids)"/> Comment
</t>
<t t-if="len(blog_post.website_message_ids) > 1">
<t t-esc="len(blog_post.website_message_ids)"/> Comments
</t>
</div>
<form id="comment" t-attf-action="/blog/#{blog_post.category_id.id}/#{blog_post.id}/post#post"
method="POST" class="form-horizontal text-center"
groups="group_website_blog_reply">
<div>
<textarea rows="4" placeholder="Your comment" class="form-control"></textarea>
</div>
<button type="submit" class="btn btn-default">Post your comment</button>
</form>
<ul class="media-list" id="comments">
<li t-foreach="blog_post.website_message_ids" t-as="message" class="media">
<div class="media-body well well-sm">
<t t-call="website.publish"><t t-set="object" t-value="message"/></t>
<t t-raw="message.body"/>
<small class="pull-left text-muted text-left">
<t t-field="message.author_id"/> on <t t-field="message.date"/>
</small>
</div>
</li>
</ul>
</template>
<!-- Page -->
@ -86,42 +96,29 @@
<t t-set="head">
<script type="text/javascript" src="/website_blog/static/src/js/website_blog.js"></script>
<link rel='stylesheet' href='/website_blog/static/src/css/website_blog.css'/>
<t t-raw="head or ''"/>
</t>
<t t-set="title">Blog</t>
<div id="wrap">
<div class="container mt48 js_website_blog">
<div class="container mt16 js_website_blog">
<div class="row">
<div class="col-md-3" id="left_column">
<h4>Categories</h4>
<ul class="nav nav-pills nav-stacked">
<t t-foreach="categories" t-as="nav_category">
<li t-att-class="'active' if category and category.id == nav_category.id else ''">
<a t-attf-href="/blog/#{nav_category.id}">
<t t-field="nav_category.name"/>
</a>
</li>
</t>
</ul>
<t t-if="category">
<a t-if="editable" t-attf-href="/blog/#{category.id}/new" class="btn btn-default">New Blog Post</a>
<t t-call="website_mail.follow"><t t-set="object" t-value="category"/></t>
</t>
</div>
<div class="col-md-9" t-if="blog_post">
<t t-call="website_blog.view_blog_post"><t t-set="blog_post" t-value="blog_post"/></t>
<div class="col-sm-3 hidden-xs" id="left_column">
</div>
<div class="col-md-9" t-if="not blog_post and blog_posts">
<ul class="media-list">
<li t-foreach="blog_posts" t-as="blog_post" data-publish="">
<t t-call="website_blog.view_blog_post_short"/>
</li>
</ul>
<div class="text-center">
<t t-call="website.pager"/>
</div>
<div class="col-lg-8 col-sm-9 col-lg-offset-1" t-if="not blog_post and blog_posts">
<t t-if="category">
<a t-if="editable" t-attf-href="/blog/#{category.id}/new" class="btn btn-default">New Blog Post</a>
</t>
<t t-foreach="blog_posts" t-as="blog_post" data-publish="">
<t t-call="website_blog.view_blog_post_short"/>
</t>
<div class="text-center" t-call="website.pager"/>
</div>
<div class="col-md-9 col-lg-8 col-lg-offset-1" t-if="blog_post">
<t t-call="website_blog.view_blog_post">
<t t-set="blog_post" t-value="blog_post"/>
</t>
</div>
</div>
</div>
</div>
@ -147,5 +144,49 @@
</ul>
</xpath>
</template>
<template id="blog_aboutus" inherit_option_id="website_blog.index" name="About">
<xpath expr="//div[@id='left_column']" position="inside">
<h4>About us</h4>
<p>
Write here a small text for <b>new visitors</b> finding your website
through your <b>blog entries</b>, referenced in Google.
</p>
<div>
<button src="/contactus" class="btn btn-primary mb32">Contact us</button>
</div>
</xpath>
</template>
<template id="blog_followus" inherit_option_id="website_blog.index" name="Follow us">
<xpath expr="//div[@id='left_column']" position="inside">
<h4>Follow us</h4>
<p class="text-muted">
Why should visitor follow you?
</p>
<t t-call="website_mail.follow"><t t-set="object" t-value="category"/></t>
<div class="mb16">
<a href="http://facebook.com/openerp"><span class="icon-facebook"/></a>
<a href="http://twitter.com/openerp"><span class="icon-twitter"/></a>
<a href="http://www.linkedin.com/groups/OpenERP-165657"><span class="icon-linkedin"/></a>
</div>
</xpath>
</template>
<template id="blog_categories" inherit_option_id="website_blog.index" name="Blogs">
<xpath expr="//div[@id='left_column']" position="inside">
<h4>Blogs</h4>
<ul class="nav nav-pills nav-stacked mb32">
<t t-foreach="categories" t-as="nav_category">
<li t-att-class="'active' if category and category.id == nav_category.id else ''">
<a t-attf-href="/blog/#{nav_category.id}">
<t t-field="nav_category.name"/>
</a>
</li>
</t>
</ul>
</xpath>
</template>
</data>
</openerp>

View File

@ -107,6 +107,7 @@ class BlogPost(osv.Model):
'website_published_datetime': fields.datetime(
'Publish Date'
),
# TDE TODO FIXME: when website_mail/mail_thread.py inheritance work -> this field won't be necessary
'website_message_ids': fields.one2many(
'mail.message', 'res_id',
domain=lambda self: [

View File

@ -1,2 +1,23 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2013-Today OpenERP SA (<http://www.openerp.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import controllers
import event
import event

View File

@ -1,15 +1,17 @@
# -*- coding: utf-8 -*-
{
'name': 'Online Events',
'category': 'Website',
'summary': 'Schedule, Promote and Sell Events',
'version': '1.0',
'description': """
OpenERP Blog
============
Online Events
=============
""",
'author': 'OpenERP SA',
'depends': ['website', 'event_sale', 'website_sale'],
'depends': ['website', 'website_mail', 'event_sale', 'website_sale'],
'data': [
'event_data.xml',
'views/website_event.xml',

View File

@ -1 +1,3 @@
# -*- coding: utf-8 -*-
import main

View File

@ -1,4 +1,23 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2013-Today OpenERP SA (<http://www.openerp.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp import SUPERUSER_ID
from openerp.addons.web import http
@ -18,7 +37,10 @@ class website_event(http.Controller):
@website.route(['/event', '/event/page/<int:page>/'], type='http', auth="public")
def events(self, page=1, **searches):
cr, uid, context = request.cr, request.uid, request.context
event_obj = request.registry['event.event']
type_obj = request.registry['event.type']
country_obj = request.registry['res.country']
searches.setdefault('date', 'all')
searches.setdefault('type', 'all')
@ -54,13 +76,19 @@ class website_event(http.Controller):
]
# search domains
current_date = dates[0][1]
current_type = None
current_country = None
for date in dates:
if searches.get("date") == date[0]:
if searches["date"] == date[0]:
domain_search["date"] = date[2]
if searches.get("type", "all") != 'all':
domain_search["type"] = [("type", "=", int(searches.get("type")))]
if searches.get("country", "all") != 'all':
domain_search["country"] = [("country_id", "=", int(searches.get("country")))]
current_date = date[1]
if searches["type"] != 'all':
current_type = type_obj.browse(cr, uid, int(searches['type']), context=context)
domain_search["type"] = [("type", "=", int(searches["type"]))]
if searches["country"] != 'all':
current_country = country_obj.browse(cr, uid, int(searches['country']), context=context)
domain_search["country"] = [("country_id", "=", int(searches["country"]))]
def dom_without(without):
domain = SUPERUSER_ID != request.uid and [('website_published', '=', True)] or [(1, "=", 1)]
@ -109,6 +137,9 @@ class website_event(http.Controller):
context=request.context)
values = {
'current_date': current_date,
'current_country': current_country,
'current_type': current_type,
'event_ids': events_ids,
'dates': dates,
'types': types,
@ -126,11 +157,9 @@ class website_event(http.Controller):
values = {
'event_id': event_obj.browse(request.cr, request.uid, event_id,
dict(request.context, show_address=1)),
'message_ids': event_obj.browse(request.cr, request.uid, event_id, request.context).message_ids,
'subscribe': post.get('subscribe'),
'range': range
}
return request.website.render("website_event.detail", values)
return request.website.render("website_event.event_description_full", values)
@website.route(['/event/<int:event_id>/add_cart'], type='http', auth="public")
def add_cart(self, event_id=None, **post):
@ -182,50 +211,3 @@ class website_event(http.Controller):
if not _values:
return werkzeug.utils.redirect("/event/%s/" % event_id)
return werkzeug.utils.redirect("/shop/checkout")
@website.route(['/event/<int:event_id>/subscribe'], type='http', auth="public")
def subscribe(self, event_id=None, **post):
partner_obj = request.registry['res.partner']
event_obj = request.registry['event.event']
user_obj = request.registry['res.users']
if event_id and 'subscribe' in post and (post.get('email') or not request.context['is_public_user']):
if request.context['is_public_user']:
partner_ids = partner_obj.search(
request.cr, SUPERUSER_ID, [("email", "=", post.get('email'))],
context=request.context)
if not partner_ids:
partner_data = {
"email": post.get('email'),
"name": "Subscribe: %s" % post.get('email')
}
partner_ids = [partner_obj.create(
request.cr, SUPERUSER_ID, partner_data, context=request.context)]
else:
partner_ids = [user_obj.browse(
request.cr, request.uid, request.uid,
context=request.context).partner_id.id]
event_obj.check_access_rule(request.cr, request.uid, [event_id],
'read', request.context)
event_obj.message_subscribe(request.cr, SUPERUSER_ID, [event_id],
partner_ids, request.context)
return self.event(event_id=event_id, subscribe=post.get('email'))
@website.route(['/event/<int:event_id>/unsubscribe'], type='http', auth="public")
def unsubscribe(self, event_id=None, **post):
partner_obj = request.registry['res.partner']
event_obj = request.registry['event.event']
user_obj = request.registry['res.users']
if event_id and 'unsubscribe' in post and (post.get('email') or not request.context['is_public_user']):
if request.context['is_public_user']:
partner_ids = partner_obj.search(
request.cr, SUPERUSER_ID, [("email", "=", post.get('email'))],
context=request.context)
else:
partner_ids = [user_obj.browse(request.cr, request.uid, request.uid, request.context).partner_id.id]
event_obj.check_access_rule(request.cr, request.uid, [event_id], 'read', request.context)
event_obj.message_unsubscribe(request.cr, SUPERUSER_ID, [event_id], partner_ids, request.context)
return self.event(event_id=event_id, subscribe=None)

View File

@ -0,0 +1,9 @@
.. _changelog:
Changelog
=========
`trunk (saas-3)`
----------------
- created ``website_event``

View File

@ -0,0 +1,10 @@
Website Event Module documentation topics
'''''''''''''''''''''''''''''''''''''''''
Changelog
'''''''''
.. toctree::
:maxdepth: 1
changelog.rst

View File

@ -1,8 +1,28 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2013-Today OpenERP SA (<http://www.openerp.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.osv import osv, fields
from openerp import SUPERUSER_ID
# defined for access rules
class product(osv.osv):
_inherit = 'product.product'
@ -15,7 +35,15 @@ class event(osv.osv):
_inherit = 'event.event'
_columns = {
'website_published': fields.boolean('Available in the website'),
'description_website': fields.html('Description for the website'),
# TDE TODO FIXME: when website_mail/mail_thread.py inheritance work -> this field won't be necessary
'website_message_ids': fields.one2many(
'mail.message', 'res_id',
domain=lambda self: [
'&', ('model', '=', self._name), ('type', '=', 'comment')
],
string='Website Messages',
help="Website communication history",
),
}
def google_map_img(self, cr, uid, ids, zoom=8, width=298, height=298, context=None):
@ -28,13 +56,14 @@ class event(osv.osv):
if partner.address_id:
return self.browse(cr, SUPERUSER_ID, ids[0], context=context).address_id.google_map_link()
class sale_order_line(osv.osv):
_inherit = "sale.order.line"
def _recalculate_product_values(self, cr, uid, ids, product_id=None, context=None):
if not ids:
return super(sale_order_line, self)._recalculate_product_values(cr, uid, ids, product_id, context=context)
order_line = self.browse(cr, uid, ids[0], context=context)
product = product_id and self.pool.get('product.product').browse(cr, uid, product_id, context=context) or order_line.product_id
res = super(sale_order_line, self)._recalculate_product_values(cr, uid, ids, product.id, context=context)

View File

@ -2,54 +2,21 @@
<openerp>
<data>
<record id="event_sale.event_technical_training" model="event.event">
<field name="website_published">True</field>
<field name="description_website"><![CDATA[
<center><strong>OpenERP v.7 Technical Training in English (5 days)</strong></center>
<p>&nbsp;</p>
<p><strong>Course summary:</strong></p>
<p>This course is dedicated to partners, integrators and developers who need to grasp knowledge of the OpenERP development process. This course is for new developers or for IT professionals eager to learn more about the OpenERP technical aspects.</p>
<p>&nbsp;</p>
<p><strong>Objectives:</strong></p>
<p>Having attended this course, participants should be able to:</p>
<ul>
<li>Understand the development concepts and architecture;</li>
<li>Install and administer OpenERP;</li>
<li>Develop a new OpenERP module.</li>
</ul>
<p>&nbsp;</p>
<p><strong>Our prices include:</strong></p>
<ul>
<li>drinks and lunch;</li>
<li>training material.</li>
</ul>
<p>&nbsp;</p>
<p><strong>Requirements:</strong></p>
<ul>
<li>Bring your own laptop.</li>
<li>Participants are expected to have some knowledge in programming.&nbsp;A basic knowledge of the Python programming is recommended.</li>
<li>Participants preferably have a functional knowledge of the OpenERP software (see Functional Training).</li>
</ul>
<p>&nbsp;</p>
<p>For more information on the program, please explore <a href="http://www.openerp.com/services/technical-training">http://www.openerp.com/services/technical-training</a>.</p>
<p>If you have a question<strong>&nbsp;concerning the content of the training</strong>, please contact <a href="mailto:training@openerp.com">training@openerp.com</a>.</p>
<p>&nbsp;</p>
<p><strong>Where to find us:</strong></p>
<p>Chaussée de Namur 40, 1367 Ramillies (Belgium)</p>
<p>More information about our Headquarter office (directions, transports, parking, hotels, ...), please have a look at&nbsp;<a href="http://bit.ly/VD8J67">http://bit.ly/VD8J67.</a></p>
<p>&nbsp;</p>
<p><strong>Cancellation Policy:&nbsp;</strong></p>
<p>The organization of the training session has related costs. Due to these costs, cancellations made less than 2 weeks (14 calendar days) prior to the start of the training session is a subject to a fee. This fee can be up to a maximum of 1000€ per cancellation request.</p>
<p>We strongly recommend to book your flight tickets and/or hotel reservations 2 weeks prior to the training. If the training is cancelled 2 weeks in advance, you'll be notified by email.</p>
<div>&nbsp;</div>
<p>For any additional information, please contact us at <a href="mailto:events@openerp.com">events@openerp.com</a>.</p>
<p>&nbsp;</p>
<p>Best regards,</p>
<p>Miss Charline Louis, OpenERP Junior Event Manager</p>
<p>&nbsp;</p>
<p align="RIGHT"><em>(OpenERP reserves the right to cancel, re-name or re-locate<br/>the event or change the dates on which it is held.)</em></p>
]]></field>
</record>
<record id="event.event_0" model="event.event">
<field name="website_published">True</field>
</record>
<record id="event.event_1" model="event.event">
<field name="website_published">True</field>
</record>
<record id="event.event_2" model="event.event">
<field name="website_published">True</field>
</record>
<record id="event.event_3" model="event.event">
<field name="website_published">True</field>
</record>
</data>
</openerp>

View File

@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Business Applications
# Copyright (c) 20123TODAY OpenERP S.A. <http://www.openerp.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.addons.website_blog.tests import test_controllers
checks = [
test_controllers,
]
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,29 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2013-Today OpenERP SA (<http://www.openerp.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.addons.mail.tests.test_mail_base import TestMailBase
from openerp.tools import mute_logger, email_split
class TestControllers(TestMailBase):
def test_00(self):
cr, uid = self.cr, self.uid

View File

@ -22,7 +22,6 @@
</template>
<!-- Page -->
<template id="index" name="Events" page="True">
<t t-call="website.layout">
<t t-set="head">
@ -30,9 +29,6 @@
</t>
<t t-set="title">Events</t>
<div id="wrap">
<div class="oe_structure">
<h1 class="text-center">Our Events</h1>
</div>
<div class="container">
<div class="row">
<div class="col-md-4 css_noprint" id="left_column">
@ -51,6 +47,17 @@
</t>
</div>
<div class="col-md-8">
<div class="oe_structure">
<h2>
Events for <t t-esc="current_date"/>
<t t-if="current_type">
in <t t-esc="current_type.name"/>
</t>
<t t-if="current_country">
in <t t-esc="current_country.name"/>
</t>
</h2>
</div>
<ul class="media-list">
<li t-foreach="event_ids" t-as="event" class="media" data-publish="">
<t t-call="website.publish"><t t-set="object" t-value="event"/></t>
@ -119,7 +126,7 @@
</xpath>
</template>
<template id="detail">
<template id="event_description_full">
<t t-call="website.layout">
<t t-set="head">
<script type="text/javascript" src="/website_event/static/src/js/website_event.js"></script>
@ -128,21 +135,10 @@
<t t-set="title">Events</t>
<div id="wrap">
<div class="container">
<t t-call="website.publish"><t t-set="object" t-value="event_id"/></t>
<div class="row">
<div class="col-md-4">
<form action="./subscribe" method="POST" class="form-inline" t-if="not subscribe">
<div class="col-lg-7">
<input placeholder="Email Address" type="email" name="email" class="form-control" t-if="is_public_user"/>
</div>
<button type="submit" class="btn btn-primary" name="subscribe">Subscribe</button>
</form>
<form action="./unsubscribe" method="POST" class="form-inline" t-if="subscribe">
<input type="hidden" name="email" t-att-value="subscribe"/>
<button type="submit" class="btn btn-default" name="unsubscribe">Unsubscribe</button>
</form>
</div>
<div class="col-md-8">
<div class="col-md-8">
<t t-call="website.publish"><t t-set="object" t-value="event_id"/></t>
<t t-call="website_mail.follow"><t t-set="object" t-value="event_id"/></t>
<h1 class="text-center" t-field="event_id.name"></h1>
<h4 class="text-center">
<i class="icon-time"></i> <span t-field="event_id.date_begin"/> to
@ -151,20 +147,19 @@
<h5 class="text-center" t-field="event_id.address_id"/>
</div>
<div class="col-md-4 css_noprint">
<div class="col-md-4 css_noprint pull-right">
<h4>When &amp; Where</h4>
<a t-att-href="event_id.google_map_link()" target="_BLANK">
<img class="thumbnail" t-att-src="event_id.google_map_img()"/>
</a>
<address>
<pre t-field="event_id.address_id"/>
<i class="icon-time"></i> <span t-field="event_id.date_begin"> </span><br/>
<i class="icon-time"></i> <span t-field="event_id.date_end"> </span>
<t t-if="event_id.organizer_id">
<h6>Organized by:</h6>
<pre><t t-field="event_id.organizer_id"/><t t-if="event_id.phone"><br/><span>&amp;#x2706;</span> <span t-field="event_id.phone"></span></t><t t-if="event_id.email"><br/><i class="icon-envelope"></i> <span t-field="event_id.email"></span></t></pre>
</t>
</address>
<address><pre t-field="event_id.address_id"/></address>
<i class="icon-time"></i> <span t-field="event_id.date_begin"> </span><br/>
<i class="icon-time"></i> <span t-field="event_id.date_end"> </span>
<t t-if="event_id.organizer_id">
<h6>Organized by:</h6>
<address><strong t-field="event_id.organizer_id.display_name"/><t t-if="event_id.phone"><br/><span>&amp;#x2706;</span> <span t-field="event_id.phone"></span></t><t t-if="event_id.email"><br/><i class="icon-envelope"></i> <span t-field="event_id.email"></span></t></address>
</t>
</div>
<div class="col-md-8">
<t t-if="event_id.event_ticket_ids">
@ -195,14 +190,14 @@
</tr>
</tbody>
</table>
<button type="submit" class="btn btn-primary" t-if="event_id.register_avail">Order Now</button>
<button type="submit" class="btn btn-primary pull-right" t-if="event_id.register_avail">Order Now</button><br/>
</form>
</t>
<hr/>
<div t-field="event_id.description_website"></div>
<div t-field="event_id.description"></div>
<hr/>
<ul class="media-list" id="comment">
<li t-foreach="message_ids" t-as="comment" class="media">
<li t-foreach="event_id.website_message_ids" t-as="comment" class="media">
<div class="media-body">
<t t-call="website.publish"><t t-set="object" t-value="comment"/></t>
<t t-raw="comment.body"/>

View File

@ -21,3 +21,4 @@
import controllers
import mail_message
import mail_thread

View File

@ -0,0 +1,37 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2013-Today OpenERP SA (<http://www.openerp.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.osv import osv, fields
class MailThread(osv.Model):
_inherit = 'mail.thread'
_columns = {
'website_message_ids': fields.one2many(
'mail.message', 'res_id',
domain=lambda self: [
'&', ('model', '=', self._name), ('type', '=', 'comment')
],
string='Website Messages',
help="Website communication history",
),
}

View File

@ -1,15 +1,16 @@
{
'name': 'Public Projects',
'name': 'Public Projects',
'category': 'Website',
'summary': 'Publish Your Public Projects',
'version': '1.0',
'description': """
OpenERP Blog
============
OpenERP Projects
================
""",
'author': 'OpenERP SA',
'depends': ['website', 'project'],
'depends': ['website_mail', 'project'],
'data': [
'views/website_project.xml',
],

View File

@ -1,13 +1,33 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2013-Today OpenERP SA (<http://www.openerp.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.osv import osv
from openerp.addons.web import http
from openerp.addons.web.http import request
from openerp.addons.website import website
from openerp.osv import osv
class Website(osv.osv):
class Website(osv.Model):
_inherit = "website"
def preprocess_request(self, cr, uid, ids, *args, **kwargs):
project_obj = request.registry['project.project']
project_ids = project_obj.search(cr, uid, [('privacy_visibility', "=", "public")], context=request.context)
@ -20,7 +40,25 @@ class Website(osv.osv):
class website_project(http.Controller):
@website.route(['/project/<int:project_id>/'], type='http', auth="public")
def blog(self, project_id=None, **post):
def project(self, project_id=None, **post):
cr, uid, context = request.cr, request.uid, request.context
project_obj = request.registry['project.project']
project = project_obj.browse(request.cr, request.uid, project_id, request.context)
return request.website.render("website_project.index", {'project_id': project})
render_values = {
'project': project
}
return request.website.render("website_project.index", render_values)
@website.route(['/project/task/<int:task_id>'], type='http', auth="public")
def task(self, task_id=None, **post):
cr, uid, context = request.cr, request.uid, request.context
task_obj = request.registry['project.task']
task = task_obj.browse(cr, uid, task_id, context=context)
render_values = {
'task': task
}
return request.website.render("website_project.task", render_values)

View File

@ -1,57 +1,62 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<data>
<!-- Layout add nav and footer -->
<template id="footer_custom" inherit_id="website.layout" name="Custom Footer">
<xpath expr="//footer//ul[@name='products']" position="inside">
<li t-foreach="website_project_ids" t-as="project"><a t-attf-href="/project/#{ project.id }/"><t t-esc="project.name"/></a></li>
</xpath>
</template>
<!-- Layout add nav and footer -->
<!-- Task -->
<template id="task" name="Task">
<t t-call="website.layout">
<t t-set="title">Task</t>
<div id="wrap">
<div class="container">
<h4 t-field="task.name"/>
</div>
</div>
</t>
</template>
<template id="footer_custom" inherit_id="website.layout" name="Custom Footer">
<xpath expr="//footer//ul[@name='products']" position="inside">
<li t-foreach="website_project_ids" t-as="project"><a t-attf-href="/project/#{ project.id }/"><t t-esc="project.name"/></a></li>
</xpath>
</template>
<!-- Page -->
<template id="kanban_card" name="Project">
<div class="thumbnail">
<a t-attf-href="/task/#{ object_id.id }/"><span t-field="object_id.name"/></a>
<div>
Assigned to <span t-field="object_id.user_id"/>
<!-- Project -->
<template id="task_kanban_card" name="TaskKanban">
<div class="thumbnail">
<a t-attf-href="/project/task/#{object_id.id}/"><span t-field="object_id.name"/></a>
<div>
Assigned to <span t-field="object_id.user_id"/>
</div>
<div>
<span t-foreach="object_id.categ_ids" t-as="categ_id" class="label">
<t t-esc="categ_id.name"/>
</span>
</div>
<small>
<i class="icon-time"></i> <span t-field="object_id.date_start"/><br/>
<t t-if="object_id.date_end">Ending Date: <span t-field="object_id.date_end"/></t>
</small>
</div>
<div>
<span t-foreach="object_id.categ_ids" t-as="categ_id" class="label">
<t t-esc="categ_id.name"/>
</span>
</div>
<small>
<i class="icon-time"></i> <span t-field="object_id.date_start"/><br/>
<t t-if="object_id.date_end">Ending Date: <span t-field="object_id.date_end"/></t>
</small>
</div>
</template>
</template>
<template id="index" name="Project">
<t t-call="website.layout">
<t t-set="title">Project</t>
<div id="wrap">
<div class="container">
<h4 t-field="project.name"/>
<t t-call="website.kanban">
<t t-set="model">project.task</t>
<t t-set="domain" t-value="[('project_id', '=', project.id)]"/>
<t t-set="column">stage_id</t>
<t t-set="template">website_project.task_kanban_card</t>
<t t-set="step">10</t>
<t t-set="scope">3</t>
</t>
</div>
</div>
</t>
</template>
<template id="index" name="Project">
<t t-call="website.layout">
<t t-set="title">Project</t>
<div id="wrap">
<div class="oe_structure"/>
<div class="container">
<h4 t-field="project_id.name"/>
<t t-call="website.kanban">
<t t-set="model">project.task</t>
<t t-set="domain" t-value="[('project_id', '=', project_id.id)]"/>
<t t-set="column">stage_id</t>
<t t-set="template">website_project.kanban_card</t>
<t t-set="step">10</t>
<t t-set="scope">3</t>
</t>
</div>
<div class="oe_structure"/>
</div>
</t>
</template>
</data>
</data>
</openerp>

View File

@ -1,2 +1,3 @@
import controllers
import product
import website_sale

View File

@ -50,10 +50,23 @@ class Website(osv.osv):
class Ecommerce(http.Controller):
def get_categories(self):
domain = [('parent_id', '=', False)]
category_obj = request.registry.get('product.public.category')
category_ids = category_obj.search(request.cr, SUPERUSER_ID, [('parent_id', '=', False)], context=request.context)
category_ids = category_obj.search(request.cr, SUPERUSER_ID, domain, context=request.context)
categories = category_obj.browse(request.cr, SUPERUSER_ID, category_ids, context=request.context)
return categories
product_obj = request.registry.get('product.product')
groups = product_obj.read_group(request.cr, SUPERUSER_ID, [("sale_ok", "=", True), ('website_published', '=', True)], ['public_categ_id'], 'public_categ_id', context=request.context)
full_category_ids = [group['public_categ_id'][0] for group in groups if group['public_categ_id']]
for cat_id in category_obj.browse(request.cr, SUPERUSER_ID, full_category_ids, context=request.context):
while cat_id.parent_id:
cat_id = cat_id.parent_id
full_category_ids.append(cat_id.id)
full_category_ids.append(1)
return (categories, full_category_ids)
@website.route(['/shop/', '/shop/category/<cat_id>/', '/shop/category/<cat_id>/page/<int:page>/', '/shop/page/<int:page>/'], type='http', auth="public")
def category(self, cat_id=0, page=0, **post):
@ -63,7 +76,7 @@ class Ecommerce(http.Controller):
product_obj = request.registry.get('product.template')
domain = [("sale_ok", "=", True)]
domain += [('website_published', '=', True)]
#domain += [('website_published', '=', True)]
if post.get("search"):
domain += ['|', '|', '|',
@ -84,7 +97,7 @@ class Ecommerce(http.Controller):
request.context['pricelist'] = self.get_pricelist()
values = {
'categories': self.get_categories(),
'get_categories': self.get_categories,
'category_id': cat_id,
'products': product_obj.browse(request.cr, SUPERUSER_ID, product_ids, context=request.context),
'search': post.get("search"),
@ -117,7 +130,7 @@ class Ecommerce(http.Controller):
'category_id': post.get('category_id') and int(post.get('category_id')) or None,
'category': category,
'search': post.get("search"),
'categories': self.get_categories(),
'get_categories': self.get_categories,
'category_list': category_list,
'product': product,
}
@ -234,6 +247,7 @@ class Ecommerce(http.Controller):
for line in order.order_line:
suggested_ids += [p.id for p in line.product_id and line.product_id.suggested_product_ids or [] for line in order.order_line]
suggested_ids = prod_obj.search(request.cr, request.uid, [('id', 'in', suggested_ids)], context=request.context)
# select 3 random products
suggested_products = []
while len(suggested_products) < 3 and suggested_ids:
@ -241,7 +255,7 @@ class Ecommerce(http.Controller):
suggested_products.append(suggested_ids.pop(index))
values = {
'categories': self.get_categories(),
'get_categories': self.get_categories,
'suggested_products': prod_obj.browse(request.cr, request.uid, suggested_products, request.context),
}
return request.website.render("website_sale.mycart", values)
@ -272,9 +286,12 @@ class Ecommerce(http.Controller):
@website.route(['/shop/checkout/'], type='http', auth="public")
def checkout(self, **post):
classic_fields = ["name", "phone", "fax", "email", "street", "city", "state_id", "zip"]
rel_fields = ['country_id', 'state_id']
order = get_current_order()
if order.state != 'draft' or not order.order_line:
if not order or order.state != 'draft' or not order.order_line:
return self.mycart(**post)
partner_obj = request.registry.get('res.partner')
@ -287,24 +304,20 @@ class Ecommerce(http.Controller):
'error': post.get("error") and dict.fromkeys(post.get("error").split(","), 'error') or {}
}
checkout = {}
checkout = dict((field_name, '') for field_name in classic_fields + rel_fields)
if not request.context['is_public_user']:
partner = user_obj.browse(request.cr, request.uid, request.uid, request.context).partner_id
partner_id = partner.id
fields = ["name", "phone", "fax", "company", "email", "street", "city", "state_id", "zip", "country_id"]
checkout = user_obj.read(request.cr, SUPERUSER_ID, [partner_id], fields, request.context)[0]
checkout.update(dict((field_name, getattr(partner, field_name)) for field_name in classic_fields if getattr(partner, field_name)))
checkout['state_id'] = partner.state_id and partner.state_id.id or ''
checkout['country_id'] = partner.country_id and partner.country_id.id or ''
checkout['company'] = partner.parent_id and partner.parent_id.name or ''
shipping_ids = partner_obj.search(request.cr, request.uid, [("parent_id", "=", partner_id), ('type', "=", 'delivery')], context=request.context)
shipping_ids = partner_obj.search(request.cr, request.uid, [("parent_id", "=", partner.id), ('type', "=", 'delivery')], context=request.context)
if shipping_ids:
for k,v in partner_obj.read(request.cr, request.uid, shipping_ids[0], request.context).items():
for k, v in partner_obj.read(request.cr, request.uid, shipping_ids[0], request.context).items():
checkout['shipping_'+k] = v or ''
checkout.update(request.session.setdefault('checkout', {}))
for k,v in checkout.items():
checkout[k] = v or ''
values['checkout'] = checkout
countries_ids = country_obj.search(request.cr, SUPERUSER_ID, [(1, "=", 1)], context=request.context)
values['countries'] = country_obj.browse(request.cr, SUPERUSER_ID, countries_ids, request.context)
states_ids = country_state_obj.search(request.cr, SUPERUSER_ID, [(1, "=", 1)], context=request.context)

View File

@ -0,0 +1,92 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2013-Today OpenERP SA (<http://www.openerp.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.osv import osv, fields
class product_pricelist(osv.Model):
_inherit = "product.pricelist"
_columns = {
'code': fields.char('Promotionnal Code', size=64, translate=True),
}
class product_template(osv.Model):
_inherit = "product.template"
_order = 'website_published,name'
_columns = {
'website_published': fields.boolean('Available in the website'),
'website_description': fields.html('Description for the website'),
'suggested_product_id': fields.many2one('product.template', 'Suggested For Product'),
'suggested_product_ids': fields.one2many('product.template', 'suggested_product_id', 'Suggested Products'),
'website_sizex': fields.selection(map(lambda x: (str(x+1),str(x+1)), range(12)), 'Size X'),
'website_sizey': fields.selection(map(lambda x: (str(x+1),str(x+1)), range(6)), 'Size Y'),
'website_product_class': fields.selection([('','Default'), ('oe_image_full','Image Full')], 'Size Y'),
}
_defaults = {
'website_sizex': '3',
'website_sizey': '2',
'website_product_class': '',
}
def recommended_products(self, cr, uid, ids, context=None):
id = ids[0]
product_ids = []
query = """
SELECT sol.product_id
FROM sale_order_line as my
LEFT JOIN sale_order_line as sol
ON sol.order_id = my.order_id
WHERE my.product_id in (%s)
AND sol.product_id not in (%s)
GROUP BY sol.product_id
ORDER BY COUNT(sol.order_id) DESC
LIMIT 10
"""
cr.execute(query, (id, id))
for p in cr.fetchall():
product_ids.append(p[0])
# search to apply access rules
product_ids = self.search(cr, uid, [("id", "in", product_ids)], limit=3)
return self.browse(cr, uid, product_ids)
def img(self, cr, uid, ids, field='image_small', context=None):
return "/website/image?model=%s&field=%s&id=%s" % (self._name, field, ids[0])
class product_product(osv.Model):
_inherit = "product.product"
def _website_url(self, cr, uid, ids, field_name, arg, context=None):
res = {}
base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url')
for id in ids:
res[id] = "%s/shop/product/%s/" % (base_url, id)
return res
_columns = {
'website_url': fields.function(_website_url, string="Website url"),
}
def img(self, cr, uid, ids, field='image_small', context=None):
temp_id = self.browse(cr, uid, ids[0], context=context).product_tmpl_id.id
return "/website/image?model=product.template&field=%s&id=%s" % (field, temp_id)

View File

@ -2,6 +2,20 @@
/* ---- Default Styles Description ---- */
.oe_product_image {
position: absolute;
}
.oe_product_image img {
max-width: 100%;
max-height: 100%;
margin: auto;
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
}
.oe_product .oe_product_image {
position: absolute;
left: 15px;
right: 15px;
top: 15px;
@ -71,18 +85,60 @@
}
/* ---- Product Sizes ---- */
.oe-height-1 {
height: 170px;
.col-md-12 .oe-height-1 {
height: 125px;
}
.col-md-12 .oe-height-2 {
height: 250px;
}
.col-md-12 .oe-height-3 {
height: 375px;
}
.col-md-12 .oe-height-4 {
height: 499.9px;
}
.oe-height-2 {
height: 255px;
.col-md-9 .oe-height-1 {
height: 95px;
}
.col-md-9 .oe-height-2 {
height: 190px;
}
.col-md-9 .oe-height-3 {
height: 285px;
}
.col-md-9 .oe-height-4 {
height: 380px;
}
.oe-height-3 {
height: 340px;
/* ---- Product list style ---- */
.oe_list_products {
border: 1px solid rgba(100, 100, 100, 0.2);
max-width: 100%;
max-height: 140px;
}
.oe-height-4 {
height: 510px;
.oe_list_products .oe_product_image {
position: absolute;
left: 15px;
right: 15px;
top: 0;
bottom: 0;
width: 150px;
}
.oe_list_products .oe_product_image img {
max-width: 100%;
max-height: 100%;
margin: auto;
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
}
.oe_list_products .oe_product_description {
bottom: 0;
position: absolute;
left: 180px;
right: 0;
border-top: 1px solid #dddddd;
}

View File

@ -4,10 +4,23 @@
.oe_product_image
position: absolute
left: 15px
right: 15px
top: 15px
bottom: 50px
img
max-width: 100%
max-height: 100%
margin: auto
position: absolute
top: 0
left: 0
bottom: 0
right: 0
.oe_product
.oe_product_image
position: absolute
left: 15px
right: 15px
top: 15px
bottom: 50px
.oe_product_description
bottom: 0
@ -72,15 +85,50 @@
/* ---- Product Sizes ---- */
.oe-height-1
height: 170px
.col-md-12
.oe-height-1
height: 125px
.oe-height-2
height: 250px
.oe-height-3
height: 375px
.oe-height-4
height: 499.9px
.col-md-9
.oe-height-1
height: 95px
.oe-height-2
height: 190px
.oe-height-3
height: 285px
.oe-height-4
height: 380px
.oe-height-2
height: 255px
.oe-height-3
height: 340px
.oe-height-4
height: 510px
/* ---- Product list style ---- */
.oe_list_products
border: 1px solid rgba(100, 100, 100, 0.2)
max-width: 100%
max-height: 140px
.oe_product_image
position: absolute
left: 15px
right: 15px
top: 0
bottom: 0
width: 150px
img
max-width: 100%
max-height: 100%
margin: auto
position: absolute
top: 0
left: 0
bottom: 0
right: 0
.oe_product_description
bottom: 0
position: absolute
left: 180px
right: 0
border-top: 1px solid #dddddd

View File

@ -68,10 +68,12 @@
<template id="categories_recursive">
<li t-att-class="category.id == category_id and 'active' or ''">
<a t-attf-href="/shop/category/#{ category.id }/" t-field="category.name"></a>
<a t-att-class="category.id not in categ[1] and 'unpublish' or ''" t-attf-href="/shop/category/#{ category.id }/" t-field="category.name"></a>
<ul t-if="category.child_id" class="nav nav-pills nav-stacked nav-hierarchy">
<t t-foreach="category.child_id" t-as="category">
<t t-call="website_sale.categories_recursive"/>
<t t-if="category.id in categ[1] or editable">
<t t-call="website_sale.categories_recursive"/>
</t>
</t>
</ul>
</li>
@ -111,7 +113,7 @@
<div class='row style_default'>
<div class="col-md-12" id="products_grid">
<t t-foreach="products" t-as="product">
<div t-attf-class="col-md-#{ product.website_sizex } oe_product oe-height-#{ product.website_sizey } #{ product.website_product_class}" t-att-data-publish="product.website_published" data-snippet-id="colmd">
<div t-attf-class="col-md-#{ search and 3 or product.website_sizex } oe_product oe-height-#{ search and 2 or product.website_sizey } #{ product.website_product_class}" t-att-data-publish="product.website_published and 'on' or 'off'" data-name="products_layout">
<div class="oe_product_description">
<a t-attf-href="/shop/product/#{ product.id }/?#{ search and ('search=%s' % search) or ''}#{ category_id and ('&amp;category_id=%s' % category_id) or ''}">
@ -119,9 +121,7 @@
</a>
<!-- This should be an option -->
<div t-if="product.description_sale" class="text-muted oe_subdescription">
<!-- TODO: replace by a smart t-field on field.text: description_sale -->
<small t-raw="product.description_sale.replace('\n','&lt;br/&gt;') "/>
<div id="product_description" t-att-data-name="product.id"/>
</div>
<div>
<b>
@ -132,9 +132,7 @@
</t>
<t t-esc="product.product_variant_ids[0].price" />
</b>
<a t-attf-href="./add_cart/?product_id=#{ product.id }">
<span class="icon-shopping-cart"/>
</a>
<span id="add_to_cart" t-att-data-name="product.id"/>
</div>
</div>
@ -157,6 +155,35 @@
</t>
</template>
<!-- Product Description-->
<template id="product_description" inherit_option_id="website_sale.products" name="Product Description">
<xpath expr="//div[@id='product_description']" position="replace">
<small>
<t t-field="product.description_sale"/>
</small>
</xpath>
</template>
<!-- Add to cart button-->
<template id="add_to_basket" inherit_option_id="website_sale.products" name="Add to Cart">
<xpath expr="//span[@id='add_to_cart']" position="replace">
<a t-attf-href="./add_cart/?product_id=#{ product.id }">
<span class="icon-shopping-cart"/>
</a>
</xpath>
</template>
<!-- List view of products -->
<template id="list_view" inherit_option_id="website_sale.products" name="List View">
<xpath expr="//div[@data-name='products_layout']" position="attributes">
<attribute name="class">col-md-12 oe_list_products oe-height-1</attribute>
</xpath>
</template>
<!-- product -->
<template id="product" name="Product">
@ -344,7 +371,8 @@
<div class="col-md-3">
<ul class="nav nav-pills nav-stacked mt16">
<li t-att-class=" '' if category_id else 'active' "><a href="/shop/">All Products</a></li>
<t t-foreach="categories" t-as="category">
<t t-set="categ" t-value="get_categories()"/>
<t t-foreach="categ[0]" t-as="category">
<t t-call="website_sale.categories_recursive"/>
</t>
</ul>
@ -397,12 +425,16 @@
<div class='row mt16'>
<t t-foreach="suggested_products" t-as="product">
<div class='col-md-2 thumbnail'>
<a t-attf-href="/shop/product/#{ product.id }/">
<div class='mt16 text-center'>
<div class='mt16 text-center'>
<a t-attf-href="/shop/product/#{ product.id }/">
<img t-att-src="product.img('image_small')"/>
<h5 t-field='product.name'></h5>
</div>
</a>
</a>
<h5>
<a t-attf-href="/shop/product/#{ product.id }/" style="display: block;">
<span t-field="product.name"/>
</a>
</h5>
</div>
</div>
</t>
</div>
@ -463,43 +495,43 @@
<div t-attf-class="form-group #{error.get('name') and 'has-error' or ''}">
<label class="col-lg-3 control-label" for="contact_name">Name and firstname</label>
<div class="col-lg-4">
<input type="text" name="name" class="form-control" t-att-value="checkout.get('name', '')"/>
<input type="text" name="name" class="form-control" t-att-value="checkout['name']"/>
</div>
</div>
<div t-attf-class="form-group #{ error.get('phone') and 'has-error' or ''}">
<label class="col-lg-3 control-label" for="contact_name">Telephone</label>
<div class="col-lg-4">
<input type="tel" name="phone" class="form-control" t-att-value="checkout.get('phone', '')"/>
<input type="tel" name="phone" class="form-control" t-att-value="checkout['phone']"/>
</div>
</div>
<div t-attf-class="form-group #{error.get('fax') and 'has-error' or ''}">
<label class="col-lg-3 control-label" for="contact_name">Fax</label>
<div class="col-lg-4">
<input type="tel" name="fax" class="form-control" t-att-value="checkout.get('fax', '')"/>
<input type="tel" name="fax" class="form-control" t-att-value="checkout['fax']"/>
</div>
</div>
<div t-attf-class="form-group #{error.get('company') and 'has-error' or ''}">
<label class="col-lg-3 control-label" for="contact_name">Company</label>
<div class="col-lg-4">
<input type="text" name="company" class="form-control" t-att-value="checkout.get('company', '')"/>
<input type="text" name="company" class="form-control" t-att-value="checkout['company']"/>
</div>
</div>
<div t-attf-class="form-group #{error.get('email') and 'has-error' or ''}">
<label class="col-lg-3 control-label" for="contact_name">Email address</label>
<div class="col-lg-4">
<input type="email" name="email" class="form-control" t-att-value="checkout.get('email', '')"/>
<input type="email" name="email" class="form-control" t-att-value="checkout['email']"/>
</div>
</div>
<div t-attf-class="form-group #{error.get('street') and 'has-error' or ''}">
<label class="col-lg-3 control-label" for="contact_name">Street</label>
<div class="col-lg-4">
<input type="text" name="street" class="form-control" t-att-value="checkout.get('street', '')"/>
<input type="text" name="street" class="form-control" t-att-value="checkout['street']"/>
</div>
</div>
<div t-attf-class="form-group #{error.get('city') and 'has-error' or ''}">
<label class="col-lg-3 control-label" for="contact_name">City</label>
<div class="col-lg-4">
<input type="text" name="city" class="form-control" t-att-value="checkout.get('city', '')"/>
<input type="text" name="city" class="form-control" t-att-value="checkout['city']"/>
</div>
</div>
<div t-attf-class="form-group #{error.get('state_id') and 'has-error' or ''}">
@ -508,7 +540,7 @@
<select name="state_id" class="form-control">
<option value="">State / Province...</option>
<t t-foreach="states or []" t-as="state">
<option t-att-value="state.id" t-att-selected="state.id == checkout.get('state_id')"><t t-esc="state.name"/></option>
<option t-att-value="state.id" t-att-selected="state.id == checkout['state_id']"><t t-esc="state.name"/></option>
</t>
</select>
</div>
@ -516,7 +548,7 @@
<div t-attf-class="form-group #{error.get('zip') and 'has-error' or ''}">
<label class="col-lg-3 control-label" for="contact_name">Zip / Postal Code</label>
<div class="col-lg-4">
<input type="text" name="zip" class="form-control" t-att-value="checkout.get('zip', '')"/>
<input type="text" name="zip" class="form-control" t-att-value="checkout['zip']"/>
</div>
</div>
<div t-attf-class="form-group #{error.get('country_id') and 'has-error' or ''}">
@ -525,7 +557,7 @@
<select name="country_id" class="form-control">
<option value="">Country...</option>
<t t-foreach="countries or []" t-as="country">
<option t-att-value="country.id" t-att-selected="country.id == checkout.get('country_id')"><t t-esc="country.name"/></option>
<option t-att-value="country.id" t-att-selected="country.id == checkout['country_id']"><t t-esc="country.name"/></option>
</t>
</select>
</div>
@ -601,7 +633,7 @@
</div>
</div>
</div>
<button type="submit" class="btn btn-default">Last stape</button>
<button type="submit" class="btn btn-default">Confirm</button>
</form>
</div>
</div>

View File

@ -20,90 +20,29 @@
##############################################################################
from openerp import SUPERUSER_ID
from openerp.osv import osv, fields
from openerp.osv import osv
class product_pricelist(osv.osv):
_inherit = "product.pricelist"
_columns = {
'code': fields.char('Promotionnal Code', size=64, translate=True),
}
class product_template(osv.osv):
_inherit = "product.template"
_columns = {
'website_published': fields.boolean('Available in the website'),
'website_description': fields.html('Description for the website'),
'suggested_product_id': fields.many2one('product.template', 'Suggested For Product'),
'suggested_product_ids': fields.one2many('product.template', 'suggested_product_id', 'Suggested Products'),
'website_sizex': fields.selection(map(lambda x: (str(x+1),str(x+1)), range(12)), 'Size X'),
'website_sizey': fields.selection(map(lambda x: (str(x+1),str(x+1)), range(6)), 'Size Y'),
'website_product_class': fields.selection([('','Default'), ('oe_image_full','Image Full')], 'Size Y'),
}
_defaults = {
'website_sizex': '3',
'website_sizey': '2',
'website_product_class': '',
}
def recommended_products(self, cr, uid, ids, context=None):
id = ids[0]
product_ids = []
query = """
SELECT sol.product_id
FROM sale_order_line as my
LEFT JOIN sale_order_line as sol
ON sol.order_id = my.order_id
WHERE my.product_id in (%s)
AND sol.product_id not in (%s)
GROUP BY sol.product_id
ORDER BY COUNT(sol.order_id) DESC
LIMIT 10
"""
cr.execute(query, (id, id))
for p in cr.fetchall():
product_ids.append(p[0])
# search to apply access rules
product_ids = self.search(cr, uid, [("id", "in", product_ids)], limit=3)
return self.browse(cr, uid, product_ids)
def img(self, cr, uid, ids, field='image_small', context=None):
return "/website/image?model=%s&field=%s&id=%s" % (self._name, field, ids[0])
class product_product(osv.osv):
_inherit = "product.product"
def _website_url(self, cr, uid, ids, field_name, arg, context=None):
res = {}
base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url')
for id in ids:
res[id] = "%s/shop/product/%s/" % (base_url, id)
return res
_columns = {
'website_url': fields.function(_website_url, string="Website url"),
}
def img(self, cr, uid, ids, field='image_small', context=None):
return "/website/image?model=%s&field=%s&id=%s" % (self._name, field, ids[0])
class sale_order(osv.osv):
class sale_order(osv.Model):
_inherit = "sale.order"
def get_total_quantity(self, cr, uid, ids, context=None):
order = self.browse(cr, uid, ids[0], context=context)
return sum(l.product_uom_qty for l in (order.order_line or []))
class sale_order_line(osv.osv):
class sale_order_line(osv.Model):
_inherit = "sale.order.line"
def _recalculate_product_values(self, cr, uid, ids, product_id=None, context=None):
if context is None:
context = {}
user_obj = self.pool.get('res.users')
product_id = product_id or ids and self.browse(cr, uid, ids[0], context=context).product_id.id
return self.product_id_change(cr, SUPERUSER_ID, [],
return self.product_id_change(
cr, SUPERUSER_ID, [],
pricelist=context.pop('pricelist'),
product=product_id,
partner_id=user_obj.browse(cr, SUPERUSER_ID, uid).partner_id.id,
context=context)['value']
context=context
)['value']