[MERGE] from trunk

bzr revid: xmo@openerp.com-20131014085459-dhbypq0hg3lzzf3j
This commit is contained in:
Xavier Morel 2013-10-14 10:54:59 +02:00
commit 2e352ce187
138 changed files with 5757 additions and 1388 deletions

View File

@ -0,0 +1,23 @@
# Spanish (Argentina) translation for openobject-addons
# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013
# This file is distributed under the same license as the openobject-addons package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2013.
#
msgid ""
msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-12-21 17:04+0000\n"
"PO-Revision-Date: 2013-10-07 21:16+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Spanish (Argentina) <es_AR@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2013-10-08 05:42+0000\n"
"X-Generator: Launchpad (build 16799)\n"
#. module: account_accountant
#: model:ir.actions.client,name:account_accountant.action_client_account_menu
msgid "Open Accounting Menu"
msgstr ""

View File

@ -0,0 +1,23 @@
# Estonian translation for openobject-addons
# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013
# This file is distributed under the same license as the openobject-addons package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2013.
#
msgid ""
msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-12-21 17:05+0000\n"
"PO-Revision-Date: 2013-10-09 14:39+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Estonian <et@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2013-10-10 04:41+0000\n"
"X-Generator: Launchpad (build 16799)\n"
#. module: account_cancel
#: view:account.invoice:0
msgid "Cancel"
msgstr ""

View File

@ -1,30 +1,28 @@
# Estonian translation for openobject-addons
# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011
# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013
# This file is distributed under the same license as the openobject-addons package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2011.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2013.
#
msgid ""
msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-12-03 16:03+0000\n"
"PO-Revision-Date: 2011-10-10 19:33+0000\n"
"Last-Translator: Aare Vesi <Unknown>\n"
"POT-Creation-Date: 2012-12-21 17:05+0000\n"
"PO-Revision-Date: 2013-10-09 14:40+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Estonian <et@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2012-12-04 05:53+0000\n"
"X-Generator: Launchpad (build 16335)\n"
"X-Launchpad-Export-Date: 2013-10-10 04:41+0000\n"
"X-Generator: Launchpad (build 16799)\n"
#. module: base_crypt
#: model:ir.model,name:base_crypt.model_res_users
#. module: auth_crypt
#: field:res.users,password_crypt:0
msgid "Encrypted Password"
msgstr "Krüpteeritud Parool"
#. module: auth_crypt
#: model:ir.model,name:auth_crypt.model_res_users
msgid "Users"
msgstr ""
#, python-format
#~ msgid "Error"
#~ msgstr "Viga"
#~ msgid "res.users"
#~ msgstr "res.users"
msgstr "Kasutajad"

View File

@ -0,0 +1,23 @@
# Estonian translation for openobject-addons
# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013
# This file is distributed under the same license as the openobject-addons package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2013.
#
msgid ""
msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-12-21 17:05+0000\n"
"PO-Revision-Date: 2013-10-09 14:34+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Estonian <et@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2013-10-10 04:41+0000\n"
"X-Generator: Launchpad (build 16799)\n"
#. module: auth_oauth_signup
#: model:ir.model,name:auth_oauth_signup.model_res_users
msgid "Users"
msgstr "Kasutajad"

View File

@ -0,0 +1,22 @@
# -*- 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 models

View File

@ -0,0 +1,45 @@
# -*- 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/>.
#
##############################################################################
{
'name': 'Partners Geo-Localization',
'version': '1.0',
'category': 'Customer Relationship Management',
'description': """
Partners geolocalization
========================
""",
'author': 'OpenERP SA',
'depends': ['crm'],
'demo': [
# 'res_partner_demo.xml',
],
'data': [
'views/res_partner_view.xml',
],
'test': [],
'installable': True,
'auto_install': False,
'images': [],
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1 @@
import res_partner

View File

@ -0,0 +1,88 @@
# -*- 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/>.
#
##############################################################################
try:
import simplejson as json
except ImportError:
import json # noqa
import urllib
from openerp.osv import osv, fields
from openerp import tools
from openerp.tools.translate import _
def geo_find(addr):
url = 'https://maps.googleapis.com/maps/api/geocode/json?sensor=false&address='
url += urllib.quote(addr.encode('utf8'))
try:
result = json.load(urllib.urlopen(url))
except Exception, e:
raise osv.except_osv(_('Network error'),
_('Cannot contact geolocation servers. Please make sure that your internet connection is up and running (%s).') % e)
if result['status'] != 'OK':
return None
try:
geo = result['results'][0]['geometry']['location']
return float(geo['lat']), float(geo['lng'])
except (KeyError, ValueError):
return None
def geo_query_address(street=None, zip=None, city=None, state=None, country=None):
if country and ',' in country and (country.endswith(' of') or country.endswith(' of the')):
# put country qualifier in front, otherwise GMap gives wrong results,
# e.g. 'Congo, Democratic Republic of the' => 'Democratic Republic of the Congo'
country = '{1} {0}'.format(*country.split(',', 1))
return tools.ustr(', '.join(filter(None, [street,
("%s %s" % (zip or '', city or '')).strip(),
state,
country])))
class res_partner(osv.osv):
_inherit = "res.partner"
_columns = {
'partner_latitude': fields.float('Geo Latitude'),
'partner_longitude': fields.float('Geo Longitude'),
'date_localization': fields.date('Geo Localization Date'),
}
def geo_localize(self, cr, uid, ids, context=None):
# Don't pass context to browse()! We need country names in english below
for partner in self.browse(cr, uid, ids):
if not partner:
continue
result = geo_find(geo_query_address(street=partner.street,
zip=partner.zip,
city=partner.city,
state=partner.state_id.name,
country=partner.country_id.name))
if result:
self.write(cr, uid, [partner.id], {
'partner_latitude': result[0],
'partner_longitude': result[1],
'date_localization': fields.date.context_today(self, cr, uid, context=context)
}, context=context)
return True

View File

@ -0,0 +1,30 @@
<?xml version="1.0"?>
<openerp>
<data>
<record id="view_crm_partner_geo_form" model="ir.ui.view">
<field name="name">res.partner.geo.inherit</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_partner_form"/>
<field name="arch" type="xml">
<xpath expr="//notebook[last()]" position="inside">
<page string="Geo Localization" name="geo_localization" groups="base.group_no_one">
<group colspan="2" col="2">
<separator string="Geo Localization" colspan="2"/>
<button
string="Geo Localize"
name="geo_localize"
colspan="2"
icon="gtk-apply"
type="object"/>
<field name="partner_latitude"/>
<field name="partner_longitude"/>
<field name="date_localization"/>
</group>
</page>
</xpath>
</field>
</record>
</data>
</openerp>

View File

@ -0,0 +1,185 @@
# Hindi translation for openobject-addons
# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013
# This file is distributed under the same license as the openobject-addons package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2013.
#
msgid ""
msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-12-21 17:05+0000\n"
"PO-Revision-Date: 2013-10-09 05:55+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Hindi <hi@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2013-10-10 04:41+0000\n"
"X-Generator: Launchpad (build 16799)\n"
#. module: base_report_designer
#: model:ir.model,name:base_report_designer.model_base_report_sxw
msgid "base.report.sxw"
msgstr "base.report.sxw"
#. module: base_report_designer
#: view:base_report_designer.installer:0
msgid "OpenERP Report Designer Configuration"
msgstr ""
#. module: base_report_designer
#: view:base_report_designer.installer:0
msgid ""
"This plug-in allows you to create/modify OpenERP Reports into OpenOffice "
"Writer."
msgstr ""
#. module: base_report_designer
#: view:base.report.sxw:0
msgid "Upload the modified report"
msgstr ""
#. module: base_report_designer
#: view:base.report.file.sxw:0
msgid "The .SXW report"
msgstr ""
#. module: base_report_designer
#: model:ir.model,name:base_report_designer.model_base_report_designer_installer
msgid "base_report_designer.installer"
msgstr ""
#. module: base_report_designer
#: model:ir.model,name:base_report_designer.model_base_report_rml_save
msgid "base.report.rml.save"
msgstr ""
#. module: base_report_designer
#: view:base_report_designer.installer:0
msgid "Configure"
msgstr ""
#. module: base_report_designer
#: view:base_report_designer.installer:0
msgid "title"
msgstr ""
#. module: base_report_designer
#: field:base.report.file.sxw,report_id:0
#: field:base.report.sxw,report_id:0
msgid "Report"
msgstr ""
#. module: base_report_designer
#: view:base.report.rml.save:0
msgid "The RML Report"
msgstr ""
#. module: base_report_designer
#: model:ir.ui.menu,name:base_report_designer.menu_action_report_designer_wizard
msgid "Report Designer"
msgstr ""
#. module: base_report_designer
#: field:base_report_designer.installer,name:0
msgid "File name"
msgstr ""
#. module: base_report_designer
#: view:base.report.file.sxw:0
#: view:base.report.sxw:0
msgid "Get a report"
msgstr ""
#. module: base_report_designer
#: view:base_report_designer.installer:0
#: model:ir.actions.act_window,name:base_report_designer.action_report_designer_wizard
msgid "OpenERP Report Designer"
msgstr ""
#. module: base_report_designer
#: view:base.report.sxw:0
msgid "Continue"
msgstr ""
#. module: base_report_designer
#: field:base.report.rml.save,file_rml:0
msgid "Save As"
msgstr ""
#. module: base_report_designer
#: help:base_report_designer.installer,plugin_file:0
msgid ""
"OpenObject Report Designer plug-in file. Save as this file and install this "
"plug-in in OpenOffice."
msgstr ""
#. module: base_report_designer
#: view:base.report.rml.save:0
msgid "Save RML FIle"
msgstr ""
#. module: base_report_designer
#: field:base.report.file.sxw,file_sxw:0
#: field:base.report.file.sxw,file_sxw_upload:0
msgid "Your .SXW file"
msgstr ""
#. module: base_report_designer
#: view:base_report_designer.installer:0
msgid "Installation and Configuration Steps"
msgstr ""
#. module: base_report_designer
#: field:base_report_designer.installer,description:0
msgid "Description"
msgstr ""
#. module: base_report_designer
#: view:base.report.file.sxw:0
msgid ""
"This is the template of your requested report.\n"
"Save it as a .SXW file and open it with OpenOffice.\n"
"Don't forget to install the OpenERP SA OpenOffice package to modify it.\n"
"Once it is modified, re-upload it in OpenERP using this wizard."
msgstr ""
#. module: base_report_designer
#: model:ir.actions.act_window,name:base_report_designer.action_view_base_report_sxw
msgid "Base Report sxw"
msgstr ""
#. module: base_report_designer
#: model:ir.model,name:base_report_designer.model_base_report_file_sxw
msgid "base.report.file.sxw"
msgstr ""
#. module: base_report_designer
#: field:base_report_designer.installer,plugin_file:0
msgid "OpenObject Report Designer Plug-in"
msgstr ""
#. module: base_report_designer
#: model:ir.actions.act_window,name:base_report_designer.action_report_designer_installer
msgid "OpenERP Report Designer Installation"
msgstr ""
#. module: base_report_designer
#: view:base.report.sxw:0
msgid "Cancel"
msgstr ""
#. module: base_report_designer
#: view:base.report.sxw:0
msgid "or"
msgstr ""
#. module: base_report_designer
#: model:ir.model,name:base_report_designer.model_ir_actions_report_xml
msgid "ir.actions.report.xml"
msgstr ""
#. module: base_report_designer
#: view:base.report.sxw:0
msgid "Select your report"
msgstr ""

View File

@ -0,0 +1,33 @@
# Estonian translation for openobject-addons
# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013
# This file is distributed under the same license as the openobject-addons package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2013.
#
msgid ""
msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-12-21 17:05+0000\n"
"PO-Revision-Date: 2013-10-09 14:42+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Estonian <et@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2013-10-10 04:41+0000\n"
"X-Generator: Launchpad (build 16799)\n"
#. module: claim_from_delivery
#: view:stock.picking.out:0
msgid "Claims"
msgstr "Nõuded"
#. module: claim_from_delivery
#: model:res.request.link,name:claim_from_delivery.request_link_claim_from_delivery
msgid "Delivery Order"
msgstr "Tarnetellimus"
#. module: claim_from_delivery
#: model:ir.actions.act_window,name:claim_from_delivery.action_claim_from_delivery
msgid "Claim From Delivery"
msgstr ""

View File

@ -21,7 +21,7 @@
{
'name': 'Partners Geo-Localization',
'name': 'Partner Assignation',
'version': '1.0',
'category': 'Customer Relationship Management',
'description': """
@ -37,8 +37,11 @@ The most appropriate partner can be assigned.
You can also use the geolocalization without using the GPS coordinates.
""",
'author': 'OpenERP SA',
'depends': ['crm', 'account', 'portal'],
'demo': ['res_partner_demo.xml', 'crm_lead_demo.xml'],
'depends': ['base_geolocalize', 'crm', 'account', 'portal'],
'demo': [
# 'res_partner_demo.xml',
'crm_lead_demo.xml'
],
'data': [
'security/ir.model.access.csv',
'res_partner_view.xml',
@ -50,6 +53,7 @@ You can also use the geolocalization without using the GPS coordinates.
'portal_data.xml',
'report/crm_lead_report_view.xml',
'report/crm_partner_report_view.xml',
'res_partner_demo.xml',
],
'js': [
'static/src/js/next.js',

View File

@ -19,46 +19,12 @@
#
##############################################################################
import urllib
import random
try:
import simplejson as json
except ImportError:
import json # noqa
from openerp.addons.base_geolocalize.models.res_partner import geo_find, geo_query_address
from openerp.osv import osv
from openerp.osv import fields
from openerp.tools.translate import _
from openerp import tools
def geo_find(addr):
url = 'https://maps.googleapis.com/maps/api/geocode/json?sensor=false&address='
url += urllib.quote(addr.encode('utf8'))
try:
result = json.load(urllib.urlopen(url))
except Exception, e:
raise osv.except_osv(_('Network error'),
_('Cannot contact geolocation servers. Please make sure that your internet connection is up and running (%s).') % e)
if result['status'] != 'OK':
return None
try:
geo = result['results'][0]['geometry']['location']
return float(geo['lat']), float(geo['lng'])
except (KeyError, ValueError):
return None
def geo_query_address(street=None, zip=None, city=None, state=None, country=None):
if country and ',' in country and (country.endswith(' of') or country.endswith(' of the')):
# put country qualifier in front, otherwise GMap gives wrong results,
# e.g. 'Congo, Democratic Republic of the' => 'Democratic Republic of the Congo'
country = '{1} {0}'.format(*country.split(',',1))
return tools.ustr(', '.join(filter(None, [street,
("%s %s" % (zip or '', city or '')).strip(),
state,
country])))
class res_partner_grade(osv.osv):
_order = 'sequence'
@ -88,9 +54,6 @@ class res_partner_activation(osv.osv):
class res_partner(osv.osv):
_inherit = "res.partner"
_columns = {
'partner_latitude': fields.float('Geo Latitude'),
'partner_longitude': fields.float('Geo Longitude'),
'date_localization': fields.date('Geo Localization Date'),
'partner_weight': fields.integer('Grade Weight',
help="Gives the probability to assign a lead to this partner. (0 means no assignation.)"),
'opportunity_assigned_ids': fields.one2many('crm.lead', 'partner_assigned_id',\
@ -100,6 +63,14 @@ class res_partner(osv.osv):
'date_partnership' : fields.date('Partnership Date'),
'date_review' : fields.date('Latest Partner Review'),
'date_review_next' : fields.date('Next Partner Review'),
# customer implementation
'assigned_partner_id': fields.many2one(
'res.partner', 'Implementedy by',
),
'implemented_partner_ids': fields.one2many(
'res.partner', 'assigned_partner_id',
string='Implementation References',
),
}
_defaults = {
'partner_weight': lambda *args: 0
@ -111,23 +82,7 @@ class res_partner(osv.osv):
partner_grade = self.pool.get('res.partner.grade').browse(cr, uid, grade_id)
res['value']['partner_weight'] = partner_grade.partner_weight
return res
def geo_localize(self, cr, uid, ids, context=None):
# Don't pass context to browse()! We need country names in english below
for partner in self.browse(cr, uid, ids):
if not partner:
continue
result = geo_find(geo_query_address(street=partner.street,
zip=partner.zip,
city=partner.city,
state=partner.state_id.name,
country=partner.country_id.name))
if result:
self.write(cr, uid, [partner.id], {
'partner_latitude': result[0],
'partner_longitude': result[1],
'date_localization': fields.date.context_today(self,cr,uid,context=context)
}, context=context)
return True
class crm_lead(osv.osv):
_inherit = "crm.lead"

View File

@ -3,25 +3,10 @@
<data>
<!-- Partner and address -->
<record id="res_partner_access" model="ir.model.access">
<field name="name">openerp.portal.res.partner</field>
<field name="model_id" ref="base.model_res_partner"/>
<field name="group_id" ref="portal.group_portal"/>
<field name="perm_read" eval="1"/>
<field name="perm_create" eval="0"/>
<field name="perm_write" eval="0"/>
<field name="perm_unlink" eval="0"/>
</record>
<record id="res_partner_rule" model="ir.rule">
<field name="name">openerp.portal.res.partner</field>
<field name="model_id" ref="base.model_res_partner"/>
<field name="groups" eval="[(6,0,[ref('portal.group_portal')])]"/>
<field name="domain_force">[('id','child_of',user.commercial_partner_id.id)]</field>
</record>
<record id="res_partner_grade_access" model="ir.model.access">
<field name="name">openerp.portal.res.partner.grade</field>
<field name="model_id" ref="crm_partner_assign.model_res_partner_grade"/>
<field name="group_id" ref="portal.group_portal"/>
<field name="group_id" ref="base.group_portal"/>
<field name="perm_read" eval="1"/>
<field name="perm_create" eval="0"/>
<field name="perm_write" eval="0"/>
@ -32,13 +17,13 @@
<record id="assigned_lead_portal_rule_1" model="ir.rule">
<field name="name">openerp.portal.crm.lead</field>
<field name="model_id" ref="crm.model_crm_lead"/>
<field name="groups" eval="[(6,0,[ref('portal.group_portal')])]"/>
<field name="groups" eval="[(6,0,[ref('base.group_portal')])]"/>
<field name="domain_force">[('partner_assigned_id','child_of',user.commercial_partner_id.id)]</field>
</record>
<record id="lead_portal_access" model="ir.model.access">
<field name="name">openerp.portal.crm.lead</field>
<field name="model_id" ref="crm.model_crm_lead"/>
<field name="group_id" ref="portal.group_portal"/>
<field name="group_id" ref="base.group_portal"/>
<field name="perm_read" eval="1"/>
<field name="perm_create" eval="0"/>
<field name="perm_write" eval="1"/>

View File

@ -9,5 +9,9 @@
<field name="grade_id" ref="res_partner_grade_first"/>
<field name="partner_weight">10</field>
</record>
<record model="res.partner" id="base.res_partner_13">
<field name="assigned_partner_id" eval="ref('base.res_partner_15')"/>
</record>
</data>
</openerp>

View File

@ -98,13 +98,14 @@
</field>
</record>
<record id="view_crm_partner_geo_form" model="ir.ui.view">
<field name="name">res.partner.geo.inherit</field>
<record id="view_crm_partner_assign_form" model="ir.ui.view">
<field name="name">res.partner.assign.inherit</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_partner_form"/>
<field name="inherit_id" ref="base_geolocalize.view_crm_partner_geo_form"/>
<field name="arch" type="xml">
<xpath expr="//notebook[last()]" position="inside">
<page string="Geo Localization">
<xpath expr="//page[@name='geo_localization']" position="replace">
<!-- <xpath expr="//notebook[last()]" position="inside"> -->
<page string="Geo Localization" name="geo_localization">
<group>
<group>
<separator string="Partner Activation" colspan="2"/>

View File

@ -5,4 +5,4 @@ access_crm_partner_report,crm.partner.report.assign.all,model_crm_partner_report
access_res_partner_grade,res.partner.grade,model_res_partner_grade,base.group_sale_salesman,1,1,1,0
access_res_partner_grade_manager,res.partner.grade.manager,model_res_partner_grade,base.group_sale_manager,1,1,1,1
"access_partner_activation_manager","res.partner.activation.manager","model_res_partner_activation","base.group_partner_manager",1,1,1,1
partner_access_crm_lead,crm.lead,model_crm_lead,portal.group_portal,1,1,0,0
partner_access_crm_lead,crm.lead,model_crm_lead,base.group_portal,1,1,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
5 access_res_partner_grade res.partner.grade model_res_partner_grade base.group_sale_salesman 1 1 1 0
6 access_res_partner_grade_manager res.partner.grade.manager model_res_partner_grade base.group_sale_manager 1 1 1 1
7 access_partner_activation_manager res.partner.activation.manager model_res_partner_activation base.group_partner_manager 1 1 1 1
8 partner_access_crm_lead crm.lead model_crm_lead portal.group_portal base.group_portal 1 1 0 0

View File

@ -87,7 +87,7 @@ class crm_lead_forward_to_partner(osv.TransientModel):
raise osv.except_osv(_('Email Template Error'),
_('The Forward Email Template is not in the database'))
try:
portal_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'portal', 'group_portal')[1]
portal_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'base', 'group_portal')[1]
except ValueError:
raise osv.except_osv(_('Portal Group Error'),
_('The Portal group cannot be found'))

View File

@ -0,0 +1,85 @@
# Estonian translation for openobject-addons
# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013
# This file is distributed under the same license as the openobject-addons package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2013.
#
msgid ""
msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-12-21 17:05+0000\n"
"PO-Revision-Date: 2013-10-09 15:39+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Estonian <et@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2013-10-10 04:41+0000\n"
"X-Generator: Launchpad (build 16799)\n"
#. module: crm_todo
#: model:ir.model,name:crm_todo.model_project_task
msgid "Task"
msgstr "Ülesanne"
#. module: crm_todo
#: view:crm.lead:0
msgid "Timebox"
msgstr "Ajalahter"
#. module: crm_todo
#: view:crm.lead:0
msgid "Lead"
msgstr ""
#. module: crm_todo
#: view:crm.lead:0
msgid "For cancelling the task"
msgstr "Ülesande katkestamiseks"
#. module: crm_todo
#: view:crm.lead:0
msgid "Next"
msgstr "Järgmine"
#. module: crm_todo
#: model:ir.actions.act_window,name:crm_todo.crm_todo_action
#: model:ir.ui.menu,name:crm_todo.menu_crm_todo
msgid "My Tasks"
msgstr "Minu ülesanded"
#. module: crm_todo
#: view:crm.lead:0
#: field:crm.lead,task_ids:0
msgid "Tasks"
msgstr "Ülesanded"
#. module: crm_todo
#: view:crm.lead:0
msgid "Done"
msgstr "Valmis"
#. module: crm_todo
#: view:crm.lead:0
msgid "Cancel"
msgstr "Katkesta"
#. module: crm_todo
#: model:ir.model,name:crm_todo.model_crm_lead
msgid "Lead/Opportunity"
msgstr ""
#. module: crm_todo
#: field:project.task,lead_id:0
msgid "Lead / Opportunity"
msgstr ""
#. module: crm_todo
#: view:crm.lead:0
msgid "For changing to done state"
msgstr ""
#. module: crm_todo
#: view:crm.lead:0
msgid "Previous"
msgstr "Eelmine"

View File

@ -227,7 +227,7 @@
</i>
</t>
</p>
<div groups="base.group_user,portal.group_portal">
<div groups="base.group_user,base.group_portal">
<t t-if="record.register_avail.raw_value gt 0 and record.register_avail.raw_value lt 9999">
<t t-if="!record.is_subscribed.raw_value">
<input t-att-id="record.id.raw_value" type="text" name="subscribe" class="no_of_seats" value="1" onchange="document.getElementById('btn_sub' +this.id).setAttribute('data-context',JSON.stringify({'ticket':this.value}))"/>

View File

@ -133,7 +133,7 @@ class hr_si_project(osv.osv_memory):
def check_state(self, cr, uid, ids, context=None):
obj_model = self.pool.get('ir.model.data')
emp_id = self.default_get(cr, uid, context)['emp_id']
emp_id = self.default_get(cr, uid, ['emp_id'], context)['emp_id']
# get the latest action (sign_in or out) for this employee
cr.execute('select action from hr_attendance where employee_id=%s and action in (\'sign_in\',\'sign_out\') order by name desc limit 1', (emp_id,))
res = (cr.fetchone() or ('sign_out',))[0]

View File

@ -2,7 +2,7 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_ls_chann1,im_livechat.channel,model_im_livechat_channel,,1,0,0,0
access_ls_chann2,im_livechat.channel,model_im_livechat_channel,group_im_livechat,1,1,1,0
access_ls_chann3,im_livechat.channel,model_im_livechat_channel,group_im_livechat_manager,1,1,1,1
access_ls_message_portal,im_livechat.im.message.portal,im.model_im_message,portal.group_portal,0,0,0,0
access_im_user_portal,im_livechat.im.user.portal,im.model_im_user,portal.group_portal,1,0,0,0
access_ls_message_portal,im_livechat.im.message.portal,im.model_im_message,base.group_portal,0,0,0,0
access_im_user_portal,im_livechat.im.user.portal,im.model_im_user,base.group_portal,1,0,0,0
access_ls_message,im_livechat.im.message,im.model_im_message,base.group_public,0,0,0,0
access_im_user,im_livechat.im.user,im.model_im_user,base.group_public,1,0,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_ls_chann1 im_livechat.channel model_im_livechat_channel 1 0 0 0
3 access_ls_chann2 im_livechat.channel model_im_livechat_channel group_im_livechat 1 1 1 0
4 access_ls_chann3 im_livechat.channel model_im_livechat_channel group_im_livechat_manager 1 1 1 1
5 access_ls_message_portal im_livechat.im.message.portal im.model_im_message portal.group_portal base.group_portal 0 0 0 0
6 access_im_user_portal im_livechat.im.user.portal im.model_im_user portal.group_portal base.group_portal 1 0 0 0
7 access_ls_message im_livechat.im.message im.model_im_message base.group_public 0 0 0 0
8 access_im_user im_livechat.im.user im.model_im_user base.group_public 1 0 0 0

View File

@ -34,15 +34,23 @@
<field name="image">/9j/4AAQSkZJRgABAQEASABIAAD/2wBDAAUDBAQEAwUEBAQFBQUGBwwIBwcHBw8LCwkMEQ8SEhEPERETFhwXExQaFRERGCEYGh0dHx8fExciJCIeJBweHx7/2wBDAQUFBQcGBw4ICA4eFBEUHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh7/wAARCACAAIADASIAAhEBAxEB/8QAHAAAAgIDAQEAAAAAAAAAAAAAAAcFBgECBAMI/8QATRAAAQIEAQQLDAUKBwEAAAAAAQIDAAQFEQYHEiExExdBU1VxkpOU0dIUFRYiMlFUVmGBkbE2QlKjwSMkRXSDobKzwsMIJTNGc4KE4f/EABoBAAIDAQEAAAAAAAAAAAAAAAAEAQIDBQb/xAAxEQABAwICCQMDBAMAAAAAAAABAAIDBBESUgUTFCExQVGRoVNh0RUisQYjceGB8PH/2gAMAwEAAhEDEQA/APsuCCCBCIIIIEIggggQiCCCBCIIIIEIggggQiCCCBCIIIIEIggggQuJ53PmVS6VlOagLXbQTckDTueSYyGmNWwt8kRytrJqU8o/VWhscQQFfNZik5XcR1mgil96Zwy2zl0ufk0Lzs3Mt5QNvKOqMZJRG0vdySVVUtp4zI/gOn82TCDTO8tcgRuGmN5a5Ij592xcZ8Mq6O12INsbGfDKujtdiE/qUPQ+PlcsfqGmyu7D5X0HsDG8t8kRjYWb/wCk3yRHz7ti4z4ZPR2uxDByN4jrVfVVe+06ZkMbEW/yaE5udn38kC/kjXGkNbFI8MaDcpml0zT1MoiYDc9R7X6phbCxvSOSINhZ3lvkiF5ldrNSpk/Tm5GedlkONuKXsarXIKQPnFI8KsQcOTfLi8lS1ji2ydkqmRuLSE+thY3pvkiMFljeW+SIQ3hTX+G5vnIPCmv8NzfORntrcqz21nRPctM7y1yBGqmmd5a5IhFeFNf4bm+ci/5KqtPVORnxPTa5pTLyQhThuQCm9r8YjSOpbI7CArR1TZHYQFclKEvmKSqzZWlCkbnjGwt5tJEdx1REVdZTTX1jW2nZBxpIUPlEwNUMtTTCiCCCLK6g2V50zPODUqZI5KEJ+aTC3y8G6KJxv/24YMorxHiEgXmH9AvuOqF9PFC7y6G7VEPtmP7cIVZ/Zd/vNcTS5vSSf4/IS9osmmfqjEotZQhavGI12AJNvbohkNUmhIlNhFMlVJta6k3Vytf74WtJmhJVBmZKSoIJuBrsQQfnDBMxmtlSlgIAuVE6APPHMpmAgrhaKazA64BN/ComIZRqRrMxLMm7SVBSPYFAKA917X9kMj/D4bd/T7GP7kLKrzYnqi9NJSUpWRYHXYAAfKGTkCNkV3/z/wBcTR22kW9/wVTRVvqALeFzbsUxa3XKRSlNJqcy20XAS2FNqWSBr1A2iP8ADLCJ/SLPR19mIDKlhep4lVJGn7EAwlQWVrA1nRrik7VuJPtSvOJ646Ms0zXkNZcL1Ms8rXkNZcJqHGWEuEWejr7MaHGOE+EWOjr7MK7atxJ9qV5xPXGNq3Ef25XnE9cZbRPk8LPaJ/TTROMMK8JMdHX2Y7aRW6VVFOppky28WgC4EtqTa+rWBfVCgOS/EQ1rlecT1xcMmmGanhuZnFTymSh5sBOYsE3uNwewGLxzylwDm2CtHPKXWcywV2qN3KdNNg6VMLSOMpMTEs6Hpdt1PkuICh7xeIfOzvF+1ojuw+oLoUgoWsZZv+EQ8w707EbkrviLz1zSAtRIQoXSm+ix1X8+iOmsuFqkTjovdEutQt7EmMMtJQlLYGhIAtxRLt+5MBcbcjKoSEplmkpGoBAAEaTdEpE6UmcpclMlOhJdYSu3FcQsa7lOr8jW56SZlKYW5eZcaQVNrKilKiBeyteiOPbZxJ6FSuZc7cImqgBsfwuLJpmjuWu/CaowvhvgCl9Fb6o6+89KtbvbJ2/4U9UKDbaxH6HSuZc7cG23iT0Sl80524BWUw/4qDTNA3gPCawwthvgCl9FR1R0SVEpEiVGSpclLFehRaYSjO47DTGcPTjtQw/IT7oQl2Ylm3VBF80KUkE2vuaYSj+O8Zd0OhFeCEhxQCe5GTYAkfZjWaWKEAlvHoAnZZ4IA12DjwsAnn3JLbw3yYwZSW3hHJhFeHeNPWEdDa7MHh3jP1gHQ2uzGG3xZT4+Vn9ThyHx8p5qlJfeG/hGhlJfeUcmEd4dYz9YB0NrswLx3jJKSrv8k2F7dxtdmI26LKfHyj6pDkPj5TtXJs/VQEnzp0GIRdTS3iFdEdYmA8GQ8h7Mu2tJztFx5KhmnQbX02vZVrKwQ6w27o8dAVo1aReKfiChleUinYgEyECXpr0uWcy+yZyhpvfRbihqQYQC1OzgYQ4KYSuyhxxIYZSpNGZSrNJSVpGaoEWC1AavZubmqIgm0SWEnM6RmGt7m3hylFf9UWiP3LKA/cuuvqIpLyQbFea3ylBP4x7Dy/fHNXyO5GkG91zTIHucSr8I90nxhxxqTYptfNWL/pbV/wBee/mKiWwPQJKpNOTdQznG0r2NLQUUgmwJJI07o1RE4v8ApZWf197+YqLDgWfb7hVJgJS62oqsNagTr/D4R50AGY3Xh9Hwxy17myb+Nh7ruxJhajJpT70gwqVeZbU4CHFLCs0XIIUTrA3N2F3DKxDVEyNNccWlLilgoShWpRI3fZC1is4AIsr6dhhilaGAA2328L6ZwR9DaL+os/wJiJEzk6SSlb+F0qBNwVMaDuxK4LI8DKOLgXkGf4EwgZvA+J1Tby04eeUFOKIOYnSCT7Y7M8xjY2zb7vhejkmdHGyzb7vhOruzJt6RhflsRgzmTf0jC3LYhJeA2KvVt/kJ64PAXFPq4/yE9cK7U/0/Cw2x/peE7TOZN/SMLc4xGDN5OCDeYwtbd8diEn4C4p9XH+Qnrg8BcU+rj/IT1wbU/wBPwp2x/p+F9JsracYQtlSVNKSChSSCkptoItuWiHr+ipSavtNuJ/hMYwYzNSeEaTKTbRafZlG0ONn6pCQLRzYzn5SmokZ2eeDLIeLZWQTpUk2Gjih577x3PsulIbxXKwYkcKOJPfFoICVImRnEX8a7TZufjbR5op8zjDD7aAUTyHFEhIHkC51XUuyUj2k2EWLJoJp+hLqs63sT1QeL4b+wiwSge5IAiITidcJenOJ1wpevE7LTk7ipvT7m3D8wI9G3rrSLayI5a+r/ADWmt31B5z4JCf64GXQlxCjewNzFnus9OpMVPDUzU8R1WbU+hllyefzdGco2dUNW5pB3Yl6Jh5imZ6mlOOOLFipXm9gEMBqh0htbqg5NHZHlum5TrWsrNtGq6jHQKVSR9aY+I6oUFO29+aUptH08LtYG/dzPueKX9VozFRZDcyi+bfMIJGaSLX+R9wipVbCU1KsuPy7yX0oSVKSU5qrDzbhh296qUd2Y+IjyeodIdQpBXNAKBBspO77oh1K1/FFXo+nqt727+vNemEVE4TooG7IMfyxFUmsr2DZeZel1vT6ltOKbUUySiLg2NvhFzkGWZGRlJJgrU1LMoaRnkZ1kiwJtovYQk5/JBiN+fmZhuo0oIdeWtOc44DYqJF/E9sXme9oAYFWZ00TGiMXPNXXbkwXvtS6EqDbjwXv1S6EqKHtN4m4SpHOOdiMbTmJuEqRzjnYhfWz9EvtFZk8f2r5tx4K36pdCVBtxYL36pdCVFD2nMTcJUjnHOxGNp3EvCVI5xzsQa2fojaKzJ4/tOzD1bka9R2KtTXFrlX75hWgpVoJBBB1aQYiMo9ARiihS9JcmnpULnWjsrSrKTrH4xrk7os3hzCcvSJ59h59pxxRUySU2UokAXAO75olqm4EsNLJICJlhZ4g6i/7rw2xxIGJdJhLoxj423qoYcyJ0Snz7c3UqhO1PYlZyG3nCpIPn0w02kIaaS22kJQkAJA1ARvBfRDoAG4K4AG4KvV1d8QyyLD8nJunlLb7MaBUReUSo94ajTqy8kmRUlUrMlI0oBIKVe4i3vMeTWI6C4gLFXkwDpAU6En4HTCUxwvN1Uva02JU2DHhMz7EsCXL6NBsL6YjxiGhD9MyHPp645puqYZmQUu1qSsrSSJhIt7dcZYxyKqZW8nBe7uKqY2ohSZg2OmyBr+MeBxtR0mxE0P2Y64i3ZXBr689dZlk5wOdaaFgfjHE5SsCKsXK7LpJBKvz1Og+bXACeqyxuHBwVkRjOjK+u+ONH/wBj1Ti2jq1OPciKomlZPk/p+W1enJ1+bXHu3Tsn41V2VOj00a/jBiPUIEjswVo8KKVm32Rzkx5HFlIvbZHeRECqTwBm27+So/8AaOuPLuDJ5e/f6V6cmDF7qdY7MFZ0YmpS9Trg/wCsavYmpbetTxv5kCK83K4ATpTXJM6N2dHXGzrOBFAjv9JDRo/PE9cAPuoxuzBWGWxBTpkgNl0E6PGTHpWZhKqHNuINwhlTnJ8b8IrUsnBEsrOar8mTrF5xOv4x3TNXozqW6ZIzzEy5NkN2Q4FeKdf7tHvESDv4q7ZORKZcEEEdNbrgrlLk6zS3qdPtBxh5NlJMJ6fyHzomFClYrfl5W/iNuJzikebSDDwjU8cUcxr9zhdVcxrhZwukSch9f3cZHmh2YE5D67f6Yk/sh2Ye8EV1EeULPZ48oSMTkSrYFjjBXNDsxovIbWlafC37odmHqIzBqY8oRqI8o7JC7RFYv9LPuR2Y3TkJqwH0s+5HVD4tBaDUx5R2RqI8o7JDKyE1g/7sHMjsxptEVj1sHMjsw/LQWg1MeUdkaiPKOyQ4yE1YD6WfcjqjRWQmr+tg5kdmH0IDxwaiPKEaiPKOyQqcg9XvpxZ9yOqLrk7yZS+GZvu+eqTlSmk6G85ICU+21oYpjU+6JETAbgBSImNNwAt4III0Wq//2Q==</field>
</record>
<!-- Join Memberships -->
<!-- Join Memberships -->
<function model="res.partner" name="create_membership_invoice" eval="(ref('base.res_partner_2'), ref('membership_0'), {'amount':180})"/>
<function model="res.partner" name="create_membership_invoice" eval="(ref('base.res_partner_10'), ref('membership_0'), {'amount':180})"/>
<function model="res.partner" name="create_membership_invoice" eval="(ref('base.res_partner_12'), ref('membership_0'), {'amount':180})"/>
<function model="res.partner" name="create_membership_invoice" eval="(ref('base.res_partner_13'), ref('membership_0'), {'amount':180})"/>
<!-- Demo data for free member -->
<record id="base.res_partner_12" model="res.partner">
<function model="res.partner" name="create_membership_invoice" eval="(ref('base.res_partner_18'), ref('membership_1'), {'amount':180})"/>
<function model="res.partner" name="create_membership_invoice" eval="(ref('base.res_partner_21'), ref('membership_1'), {'amount':180})"/>
<function model="res.partner" name="create_membership_invoice" eval="(ref('base.res_partner_9'), ref('membership_2'), {'amount':180})"/>
<!-- Demo data for free member -->
<record id="base.res_partner_18" model="res.partner">
<field name="free_member">True</field>
</record>
<!-- Demo data for associate member -->
<!-- Demo data for associate member -->
<record id="base.res_partner_9" model="res.partner">
<field name="associate_member" ref="base.res_partner_2"/>
</record>

View File

@ -143,7 +143,7 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
var self = this;
this._super();
this.$el.click(function(){
self.order.selectLine(this.model);
self.order.selectLine(self.model);
self.trigger('order_line_selected');
});
if(this.model.is_selected()){

View File

@ -49,7 +49,6 @@ very handy when used in combination with the module 'share'.
'wizard/share_wizard_view.xml',
'acquirer_view.xml',
'security/ir.model.access.csv',
'security/portal_security.xml',
],
'demo': ['portal_demo.xml'],
'css': ['static/src/css/portal.css'],

View File

@ -3,7 +3,7 @@
<data noupdate="1">
<!-- The portal group -->
<record id="group_portal" model="res.groups">
<record id="base.group_portal" model="res.groups">
<field name="name">Portal</field>
<field name="comment">Portal members have specific access rights (such as record rules and restricted menus).
They usually do not belong to the usual OpenERP groups.</field>

View File

@ -22,7 +22,7 @@ Mr Demo Portal</field>
</record>
<!-- Add the demo user to the portal (and therefore to the portal member group) -->
<record id="group_portal" model="res.groups">
<record id="base.group_portal" model="res.groups">
<field name="users" eval="[(4,ref('demo_user0'))]"/>
</record>

View File

@ -5,10 +5,10 @@
<!-- Top menu item -->
<menuitem name="Portal"
id="portal_menu"
groups="portal.group_portal"
groups="base.group_portal"
sequence="15"/>
<menuitem name="Messaging" id="portal_messages" parent="portal_menu" sequence="10" groups="portal.group_portal"/>
<menuitem name="Messaging" id="portal_messages" parent="portal_menu" sequence="10" groups="base.group_portal"/>
<record id="action_mail_inbox_feeds_portal" model="ir.actions.client">
<field name="name">Inbox</field>
@ -99,9 +99,9 @@
</record>
<menuitem name="Inbox" id="portal_inbox" parent="portal_messages"
action="action_mail_inbox_feeds_portal" sequence="10" groups="portal.group_portal"/>
action="action_mail_inbox_feeds_portal" sequence="10" groups="base.group_portal"/>
<menuitem name="Archives" id="portal_mail_archivesfeeds" parent="portal_messages"
action="action_mail_archives_feeds_portal" sequence="30" groups="portal.group_portal"/>
action="action_mail_archives_feeds_portal" sequence="30" groups="base.group_portal"/>
<!--
Create menu items that we'll leave empty for now - they'll be

View File

@ -1,9 +1,8 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_mail_message_portal,mail.message.portal,mail.model_mail_message,group_portal,1,1,1,1
access_mail_mail_portal,mail.mail.portal,mail.model_mail_mail,group_portal,1,1,1,0
access_mail_notification_portal,mail.notification.portal,mail.model_mail_notification,group_portal,1,1,1,0
access_mail_followers_portal,mail.followers.portal,mail.model_mail_followers,group_portal,1,1,0,0
access_res_partner_portal,res.partner.portal,base.model_res_partner,portal.group_portal,1,0,0,0
access_mail_message_portal,mail.message.portal,mail.model_mail_message,base.group_portal,1,1,1,1
access_mail_mail_portal,mail.mail.portal,mail.model_mail_mail,base.group_portal,1,1,1,0
access_mail_notification_portal,mail.notification.portal,mail.model_mail_notification,base.group_portal,1,1,1,0
access_mail_followers_portal,mail.followers.portal,mail.model_mail_followers,base.group_portal,1,1,0,0
access_acquirer,portal.payment.acquirer,portal.model_portal_payment_acquirer,,1,0,0,0
access_acquirer_all,portal.payment.acquirer,portal.model_portal_payment_acquirer,base.group_system,1,1,1,1
access_ir_attachment_group_portal,ir.attachment group_portal,base.model_ir_attachment,portal.group_portal,1,0,1,0
access_ir_attachment_group_portal,ir.attachment group_portal,base.model_ir_attachment,base.group_portal,1,0,1,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_mail_message_portal mail.message.portal mail.model_mail_message group_portal base.group_portal 1 1 1 1
3 access_mail_mail_portal mail.mail.portal mail.model_mail_mail group_portal base.group_portal 1 1 1 0
4 access_mail_notification_portal mail.notification.portal mail.model_mail_notification group_portal base.group_portal 1 1 1 0
5 access_mail_followers_portal mail.followers.portal mail.model_mail_followers group_portal base.group_portal 1 1 0 0
access_res_partner_portal res.partner.portal base.model_res_partner portal.group_portal 1 0 0 0
6 access_acquirer portal.payment.acquirer portal.model_portal_payment_acquirer 1 0 0 0
7 access_acquirer_all portal.payment.acquirer portal.model_portal_payment_acquirer base.group_system 1 1 1 1
8 access_ir_attachment_group_portal ir.attachment group_portal base.model_ir_attachment portal.group_portal base.group_portal 1 0 1 0

View File

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data noupdate="1">
<record id="portal_read_own_res_partner" model="ir.rule">
<field name="name">res_partner: read access on my partner</field>
<field name="model_id" ref="base.model_res_partner"/>
<field name="domain_force">[('id', 'child_of', user.commercial_partner_id.id)]</field>
<field name="groups" eval="[(4, ref('group_portal')), (4, ref('base.group_public'))]"/>
<field name="perm_create" eval="False"/>
<field name="perm_unlink" eval="False"/>
<field name="perm_write" eval="False"/>
</record>
</data>
</openerp>

View File

@ -31,7 +31,7 @@ class test_portal(TestMail):
cr, uid = self.cr, self.uid
# Find Portal group
group_portal = self.registry('ir.model.data').get_object(cr, uid, 'portal', 'group_portal')
group_portal = self.registry('ir.model.data').get_object(cr, uid, 'base', 'group_portal')
self.group_portal_id = group_portal.id
# Create Chell (portal user)

View File

@ -1,3 +1,3 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_crm_claim,crm.claim,crm_claim.model_crm_claim,portal.group_portal,1,0,1,0
access_crm_claim_stage,crm.claim.stage,crm_claim.model_crm_claim_stage,portal.group_portal,1,0,0,0
access_crm_claim,crm.claim,crm_claim.model_crm_claim,base.group_portal,1,0,1,0
access_crm_claim_stage,crm.claim.stage,crm_claim.model_crm_claim_stage,base.group_portal,1,0,0,0

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_crm_claim crm.claim crm_claim.model_crm_claim portal.group_portal base.group_portal 1 0 1 0
3 access_crm_claim_stage crm.claim.stage crm_claim.model_crm_claim_stage portal.group_portal base.group_portal 1 0 0 0

View File

@ -6,7 +6,7 @@
<field name="name">Portal Personal Claims</field>
<field ref="crm_claim.model_crm_claim" name="model_id"/>
<field name="domain_force">[('message_follower_ids','in', [user.partner_id.id])]</field>
<field name="groups" eval="[(4, ref('portal.group_portal'))]"/>
<field name="groups" eval="[(4, ref('base.group_portal'))]"/>
<field eval="1" name="perm_unlink"/>
<field eval="1" name="perm_write"/>
<field eval="1" name="perm_read"/>

View File

@ -1,5 +1,5 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_event,event,event.model_event_event,portal.group_portal,1,0,0,0
access_registration,registration,event.model_event_registration,portal.group_portal,1,1,1,1
access_event,event,event.model_event_event,base.group_portal,1,0,0,0
access_registration,registration,event.model_event_registration,base.group_portal,1,1,1,1
access_event,event,event.model_event_event,base.group_public,1,0,0,0
access_registration,registration,event.model_event_registration,base.group_public,1,1,1,1

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_event event event.model_event_event portal.group_portal base.group_portal 1 0 0 0
3 access_registration registration event.model_event_registration portal.group_portal base.group_portal 1 1 1 1
4 access_event event event.model_event_event base.group_public 1 0 0 0
5 access_registration registration event.model_event_registration base.group_public 1 1 1 1

View File

@ -10,14 +10,14 @@
('message_follower_ids', 'in', [user.partner_id.id])
]
</field>
<field name="groups" eval="[(4, ref('portal.group_portal')), (4, ref('base.group_public'))]"/>
<field name="groups" eval="[(4, ref('base.group_portal')), (4, ref('base.group_public'))]"/>
</record>
<record model="ir.rule" id="event_registration_website_rule">
<field name="name">Event/Registration: portal and public users: personal only</field>
<field name="model_id" ref="event.model_event_registration"/>
<field name="domain_force">[('user_id', '=', user.id)]</field>
<field name="groups" eval="[(4, ref('portal.group_portal')), (4, ref('base.group_public'))]"/>
<field name="groups" eval="[(4, ref('base.group_portal')), (4, ref('base.group_public'))]"/>
</record>
</data>

View File

@ -1,3 +1,3 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_hr_employee_portal,hr.employee user,hr.model_hr_employee,portal.group_portal,1,0,0,0
access_hr_employee_portal,hr.employee user,hr.model_hr_employee,base.group_portal,1,0,0,0
access_hr_employee_public,hr.employee user,hr.model_hr_employee,base.group_public,1,0,0,0

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_hr_employee_portal hr.employee user hr.model_hr_employee portal.group_portal base.group_portal 1 0 0 0
3 access_hr_employee_public hr.employee user hr.model_hr_employee base.group_public 1 0 0 0

View File

@ -1,10 +1,10 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_project,project,project.model_project_project,portal.group_portal,1,0,0,0
access_task,task,project.model_project_task,portal.group_portal,1,0,0,0
access_task_type,task_type,project.model_project_task_type,portal.group_portal,1,0,0,0
access_task_work,task_work,project.model_project_task_work,portal.group_portal,1,0,0,0
access_project_category,project_category,project.model_project_category,portal.group_portal,1,0,0,0
access_account_analytic_account,account_analytic_account,analytic.model_account_analytic_account,portal.group_portal,1,0,0,0
access_project,project,project.model_project_project,base.group_portal,1,0,0,0
access_task,task,project.model_project_task,base.group_portal,1,0,0,0
access_task_type,task_type,project.model_project_task_type,base.group_portal,1,0,0,0
access_task_work,task_work,project.model_project_task_work,base.group_portal,1,0,0,0
access_project_category,project_category,project.model_project_category,base.group_portal,1,0,0,0
access_account_analytic_account,account_analytic_account,analytic.model_account_analytic_account,base.group_portal,1,0,0,0
access_project_public,project,project.model_project_project,base.group_public,1,0,0,0
access_task_public,task,project.model_project_task,base.group_public,1,0,0,0
access_task_type_public,task_type,project.model_project_task_type,base.group_public,1,0,0,0

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_project project project.model_project_project portal.group_portal base.group_portal 1 0 0 0
3 access_task task project.model_project_task portal.group_portal base.group_portal 1 0 0 0
4 access_task_type task_type project.model_project_task_type portal.group_portal base.group_portal 1 0 0 0
5 access_task_work task_work project.model_project_task_work portal.group_portal base.group_portal 1 0 0 0
6 access_project_category project_category project.model_project_category portal.group_portal base.group_portal 1 0 0 0
7 access_account_analytic_account account_analytic_account analytic.model_account_analytic_account portal.group_portal base.group_portal 1 0 0 0
8 access_project_public project project.model_project_project base.group_public 1 0 0 0
9 access_task_public task project.model_project_task base.group_public 1 0 0 0
10 access_task_type_public task_type project.model_project_task_type base.group_public 1 0 0 0

View File

@ -22,7 +22,7 @@
('privacy_visibility', '=', 'followers'),
('message_follower_ids', 'in', [user.partner_id.id]),
]</field>
<field name="groups" eval="[(4, ref('portal.group_portal'))]"/>
<field name="groups" eval="[(4, ref('base.group_portal'))]"/>
</record>
<record model="ir.rule" id="project_public_rule">
@ -54,7 +54,7 @@
('project_id.privacy_visibility', '=', 'followers'),
('message_follower_ids', 'in', [user.partner_id.id]),
]</field>
<field name="groups" eval="[(4, ref('portal.group_portal'))]"/>
<field name="groups" eval="[(4, ref('base.group_portal'))]"/>
</record>
<record model="ir.rule" id="task_public_rule">

View File

@ -31,7 +31,7 @@ class TestPortalProjectBase(TestProjectBase):
cr, uid = self.cr, self.uid
# Find Portal group
group_portal_ref = self.registry('ir.model.data').get_object_reference(cr, uid, 'portal', 'group_portal')
group_portal_ref = self.registry('ir.model.data').get_object_reference(cr, uid, 'base', 'group_portal')
self.group_portal_id = group_portal_ref and group_portal_ref[1] or False
# Find Public group

View File

@ -1,4 +1,4 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_issues,project_issue,project_issue.model_project_issue,portal.group_portal,1,0,0,0
access_case_section,crm_case_section,crm.model_crm_case_section,portal.group_portal,1,0,0,0
access_issues,project_issue,project_issue.model_project_issue,base.group_portal,1,0,0,0
access_case_section,crm_case_section,crm.model_crm_case_section,base.group_portal,1,0,0,0
access_issues_public,project_issue,project_issue.model_project_issue,base.group_public,1,0,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_issues project_issue project_issue.model_project_issue portal.group_portal base.group_portal 1 0 0 0
3 access_case_section crm_case_section crm.model_crm_case_section portal.group_portal base.group_portal 1 0 0 0
4 access_issues_public project_issue project_issue.model_project_issue base.group_public 1 0 0 0

View File

@ -14,7 +14,7 @@
('project_id.privacy_visibility', '=', 'followers'),
('message_follower_ids', 'in', [user.partner_id.id]),
]</field>
<field name="groups" eval="[(4, ref('portal.group_portal'))]"/>
<field name="groups" eval="[(4, ref('base.group_portal'))]"/>
</record>
<record model="ir.rule" id="project_issue.issue_user_rule">

View File

@ -1,3 +1,3 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_issues,project_phase,project_long_term.model_project_phase,portal.group_portal,1,0,0,0
access_issues,project_phase,project_long_term.model_project_phase,base.group_portal,1,0,0,0
access_issues_public,project_phase_public,project_long_term.model_project_phase,base.group_public,1,0,0,0

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_issues project_phase project_long_term.model_project_phase portal.group_portal base.group_portal 1 0 0 0
3 access_issues_public project_phase_public project_long_term.model_project_phase base.group_public 1 0 0 0

View File

@ -6,7 +6,7 @@
<field name="name">Project/Phase: portal users: public or (portal and colleagues following) or (followers and following)</field>
<field name="model_id" ref="project_long_term.model_project_phase"/>
<field name="domain_force">[('project_id.privacy_visibility', 'in', ['public', 'portal'])]</field>
<field name="groups" eval="[(4, ref('portal.group_portal'))]"/>
<field name="groups" eval="[(4, ref('base.group_portal'))]"/>
</record>
<record model="ir.rule" id="project_phase_public_rule">

View File

@ -1,20 +1,20 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_sale_order,sale.order,sale.model_sale_order,portal.group_portal,1,0,0,0
access_sale_order_line,sale.order.line,sale.model_sale_order_line,portal.group_portal,1,0,0,0
access_account_invoice,account.invoice,account.model_account_invoice,portal.group_portal,1,0,0,0
access_account_invoice_tax,account.invoice.tax,account.model_account_invoice_tax,portal.group_portal,1,0,0,0
access_account_invoice_line,account.invoice.line,account.model_account_invoice_line,portal.group_portal,1,0,0,0
access_account_journal,account.journal,account.model_account_journal,portal.group_portal,1,0,0,0
access_account_voucher,account.voucher,account_voucher.model_account_voucher,portal.group_portal,1,0,0,0
access_account_voucher_line,account.voucher.line,account_voucher.model_account_voucher_line,portal.group_portal,1,0,0,0
access_account_move,account.move,account.model_account_move,portal.group_portal,1,0,0,0
access_account_move_line,account.move.line,account.model_account_move_line,portal.group_portal,1,0,0,0
access_account_move_reconcile,account.move.reconcile,account.model_account_move_reconcile,portal.group_portal,1,0,0,0
access_account_fiscalyear,account.sequence.fiscalyear,account.model_account_sequence_fiscalyear,portal.group_portal,1,0,0,0
access_product_list,product.pricelist,product.model_product_pricelist,portal.group_portal,1,0,0,0
access_res_partner,res.partner,base.model_res_partner,portal.group_portal,1,0,0,0
access_account_tax,account.tax,account.model_account_tax,portal.group_portal,1,0,0,0
access_account_fiscalyear,account.fiscalyear,account.model_account_fiscalyear,portal.group_portal,1,0,0,0
access_res_partner_category,res.partner.category,base.model_res_partner_category,portal.group_portal,1,0,0,0
access_account_period,account.period,account.model_account_period,portal.group_portal,1,0,0,0
access_account_account,account.account,account.model_account_account,portal.group_portal,1,0,0,0
access_sale_order,sale.order,sale.model_sale_order,base.group_portal,1,0,0,0
access_sale_order_line,sale.order.line,sale.model_sale_order_line,base.group_portal,1,0,0,0
access_account_invoice,account.invoice,account.model_account_invoice,base.group_portal,1,0,0,0
access_account_invoice_tax,account.invoice.tax,account.model_account_invoice_tax,base.group_portal,1,0,0,0
access_account_invoice_line,account.invoice.line,account.model_account_invoice_line,base.group_portal,1,0,0,0
access_account_journal,account.journal,account.model_account_journal,base.group_portal,1,0,0,0
access_account_voucher,account.voucher,account_voucher.model_account_voucher,base.group_portal,1,0,0,0
access_account_voucher_line,account.voucher.line,account_voucher.model_account_voucher_line,base.group_portal,1,0,0,0
access_account_move,account.move,account.model_account_move,base.group_portal,1,0,0,0
access_account_move_line,account.move.line,account.model_account_move_line,base.group_portal,1,0,0,0
access_account_move_reconcile,account.move.reconcile,account.model_account_move_reconcile,base.group_portal,1,0,0,0
access_account_fiscalyear,account.sequence.fiscalyear,account.model_account_sequence_fiscalyear,base.group_portal,1,0,0,0
access_product_list,product.pricelist,product.model_product_pricelist,base.group_portal,1,0,0,0
access_res_partner,res.partner,base.model_res_partner,base.group_portal,1,0,0,0
access_account_tax,account.tax,account.model_account_tax,base.group_portal,1,0,0,0
access_account_fiscalyear,account.fiscalyear,account.model_account_fiscalyear,base.group_portal,1,0,0,0
access_res_partner_category,res.partner.category,base.model_res_partner_category,base.group_portal,1,0,0,0
access_account_period,account.period,account.model_account_period,base.group_portal,1,0,0,0
access_account_account,account.account,account.model_account_account,base.group_portal,1,0,0,0

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_sale_order sale.order sale.model_sale_order portal.group_portal base.group_portal 1 0 0 0
3 access_sale_order_line sale.order.line sale.model_sale_order_line portal.group_portal base.group_portal 1 0 0 0
4 access_account_invoice account.invoice account.model_account_invoice portal.group_portal base.group_portal 1 0 0 0
5 access_account_invoice_tax account.invoice.tax account.model_account_invoice_tax portal.group_portal base.group_portal 1 0 0 0
6 access_account_invoice_line account.invoice.line account.model_account_invoice_line portal.group_portal base.group_portal 1 0 0 0
7 access_account_journal account.journal account.model_account_journal portal.group_portal base.group_portal 1 0 0 0
8 access_account_voucher account.voucher account_voucher.model_account_voucher portal.group_portal base.group_portal 1 0 0 0
9 access_account_voucher_line account.voucher.line account_voucher.model_account_voucher_line portal.group_portal base.group_portal 1 0 0 0
10 access_account_move account.move account.model_account_move portal.group_portal base.group_portal 1 0 0 0
11 access_account_move_line account.move.line account.model_account_move_line portal.group_portal base.group_portal 1 0 0 0
12 access_account_move_reconcile account.move.reconcile account.model_account_move_reconcile portal.group_portal base.group_portal 1 0 0 0
13 access_account_fiscalyear account.sequence.fiscalyear account.model_account_sequence_fiscalyear portal.group_portal base.group_portal 1 0 0 0
14 access_product_list product.pricelist product.model_product_pricelist portal.group_portal base.group_portal 1 0 0 0
15 access_res_partner res.partner base.model_res_partner portal.group_portal base.group_portal 1 0 0 0
16 access_account_tax account.tax account.model_account_tax portal.group_portal base.group_portal 1 0 0 0
17 access_account_fiscalyear account.fiscalyear account.model_account_fiscalyear portal.group_portal base.group_portal 1 0 0 0
18 access_res_partner_category res.partner.category base.model_res_partner_category portal.group_portal base.group_portal 1 0 0 0
19 access_account_period account.period account.model_account_period portal.group_portal base.group_portal 1 0 0 0
20 access_account_account account.account account.model_account_account portal.group_portal base.group_portal 1 0 0 0

View File

@ -10,7 +10,7 @@ on Sale Orders and Customer Invoices. These options are meant for customers who
their documents through the portal.</field>
</record>
<!-- Make the payment_options group implied for all portal users -->
<record id="portal.group_portal" model="res.groups">
<record id="base.group_portal" model="res.groups">
<field name="implied_ids" eval="[(4,ref('group_payment_options'))]"/>
</record>
@ -19,7 +19,7 @@ their documents through the portal.</field>
<field name="name">Portal Personal Quotations/Sales Orders</field>
<field name="model_id" ref="sale.model_sale_order"/>
<field name="domain_force">[('message_follower_ids','in',[user.partner_id.id])]</field>
<field name="groups" eval="[(4, ref('portal.group_portal'))]"/>
<field name="groups" eval="[(4, ref('base.group_portal'))]"/>
<field eval="1" name="perm_unlink"/>
<field eval="1" name="perm_write"/>
<field eval="1" name="perm_read"/>
@ -30,7 +30,7 @@ their documents through the portal.</field>
<field name="name">Portal Personal Account Invoices</field>
<field name="model_id" ref="account.model_account_invoice"/>
<field name="domain_force">[('message_follower_ids','in',[user.partner_id.id])]</field>
<field name="groups" eval="[(4, ref('portal.group_portal'))]"/>
<field name="groups" eval="[(4, ref('base.group_portal'))]"/>
</record>
<record id="portal_personal_contact" model="ir.rule">
@ -38,7 +38,7 @@ their documents through the portal.</field>
<field name="model_id" ref="base.model_res_partner"/>
<field name="domain_force">[('message_follower_ids','in',[user.partner_id.id])]</field>
<field eval="0" name="perm_read"/>
<field name="groups" eval="[(4, ref('portal.group_portal'))]"/>
<field name="groups" eval="[(4, ref('base.group_portal'))]"/>
</record>
</data>

View File

@ -1,5 +1,5 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_stock_picking,stock.picking,stock.model_stock_picking,portal.group_portal,1,0,0,0
access_stock_picking.out,stock.picking.out,stock.model_stock_picking_out,portal.group_portal,1,0,0,0
access_stock_move,stock.move,stock.model_stock_move,portal.group_portal,1,0,0,0
access_stock_warehouse_orderpoint,stock.warehouse.orderpoint,procurement.model_stock_warehouse_orderpoint,portal.group_portal,1,0,0,0
access_stock_picking,stock.picking,stock.model_stock_picking,base.group_portal,1,0,0,0
access_stock_picking.out,stock.picking.out,stock.model_stock_picking_out,base.group_portal,1,0,0,0
access_stock_move,stock.move,stock.model_stock_move,base.group_portal,1,0,0,0
access_stock_warehouse_orderpoint,stock.warehouse.orderpoint,procurement.model_stock_warehouse_orderpoint,base.group_portal,1,0,0,0

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_stock_picking stock.picking stock.model_stock_picking portal.group_portal base.group_portal 1 0 0 0
3 access_stock_picking.out stock.picking.out stock.model_stock_picking_out portal.group_portal base.group_portal 1 0 0 0
4 access_stock_move stock.move stock.model_stock_move portal.group_portal base.group_portal 1 0 0 0
5 access_stock_warehouse_orderpoint stock.warehouse.orderpoint procurement.model_stock_warehouse_orderpoint portal.group_portal base.group_portal 1 0 0 0

View File

@ -7,14 +7,14 @@
<field name="name">Portal Personal Pickings</field>
<field name="model_id" ref="stock.model_stock_picking"/>
<field name="domain_force">[('message_follower_ids','in',[user.partner_id.id])]</field>
<field name="groups" eval="[(4, ref('portal.group_portal'))]"/>
<field name="groups" eval="[(4, ref('base.group_portal'))]"/>
</record>
<record id="portal_stock_picking_out_user_rule" model="ir.rule">
<field name="name">Portal Personal Out Pickings</field>
<field name="model_id" ref="stock.model_stock_picking_out"/>
<field name="domain_force">[('message_follower_ids','in',[user.partner_id.id])]</field>
<field name="groups" eval="[(4, ref('portal.group_portal'))]"/>
<field name="groups" eval="[(4, ref('base.group_portal'))]"/>
</record>
</data>

View File

@ -93,8 +93,7 @@
</div>
</div>
<div class="oe_right oe_button_box" name="buttons" groups="base.group_user">
<button name="%(act_project_project_2_project_task_all)d" string="Tasks"
type="action" attrs="{'invisible':[('use_tasks','=', 0)]}"/>
<field name="tasks" widget="x2many_counter" string="Tasks" attrs="{'invisible':[('use_tasks','=', False)]}" options='{"views": [[false, "kanban"], [false, "tree"], [false, "form"], [false, "calendar"], [false, "gantt"], [false, "graph"]]}'/>
<button name="attachment_tree_view" string="Documents" type="object"/>
</div>
<group>

2132
addons/sale/i18n/hi.po Normal file

File diff suppressed because it is too large Load Diff

View File

@ -12,14 +12,14 @@ OpenERP Website CMS
'depends': ['web', 'share'],
'installable': True,
'data': [
'views/views.xml',
'data/website_data.xml',
'security/ir.model.access.csv',
'views/website_templates.xml',
'views/website_views.xml',
'views/snippets.xml',
'views/themes.xml',
'website_data.xml',
'website_view.xml',
'security/ir.model.access.csv',
],
'demo': [
'website_demo.xml',
'data/website_demo.xml',
]
}

View File

@ -7,6 +7,7 @@ import json
import logging
import os
import datetime
import re
from sys import maxint
@ -17,7 +18,16 @@ import werkzeug.utils
import werkzeug.wrappers
from PIL import Image
try:
from slugify import slugify
except ImportError:
def slugify(s, max_length=None):
spaceless = re.sub(r'\s+', '-', s)
specialless = re.sub(r'[^-_a-z0-9]', '', spaceless)
return specialless[:max_length]
import openerp
from openerp.osv import fields
from openerp.addons.website.models import website
from openerp.addons.web import http
from openerp.addons.web.http import request
@ -48,12 +58,9 @@ class Website(openerp.addons.web.controllers.main.Home):
# FIXME: auth, if /pagenew known anybody can create new empty page
@website.route('/pagenew/<path:path>', type='http', auth="admin")
def pagenew(self, path, noredirect=NOPE):
if '.' in path:
module, idname = path.split('.', 1)
else:
module = 'website'
idname = path
xid = "%s.%s" % (module, idname)
module = 'website'
# completely arbitrary max_length
idname = slugify(path, max_length=50)
request.cr.execute('SAVEPOINT pagenew')
imd = request.registry['ir.model.data']
@ -65,8 +72,9 @@ class Website(openerp.addons.web.controllers.main.Home):
newview = view.browse(
request.cr, request.uid, newview_id, context=request.context)
newview.write({
'arch': newview.arch.replace("website.default_page", xid),
'name': "page/%s" % path,
'arch': newview.arch.replace("website.default_page",
"%s.%s" % (module, idname)),
'name': path,
'page': True,
})
# Fuck it, we're doing it live
@ -79,10 +87,13 @@ class Website(openerp.addons.web.controllers.main.Home):
'noupdate': True
}, context=request.context)
except psycopg2.IntegrityError:
logger.exception('Unable to create ir_model_data for page %s', path)
request.cr.execute('ROLLBACK TO SAVEPOINT pagenew')
return werkzeug.exceptions.InternalServerError()
else:
request.cr.execute('RELEASE SAVEPOINT pagenew')
url = "/page/%s" % path
url = "/page/%s" % idname
if noredirect is not NOPE:
return werkzeug.wrappers.Response(url, mimetype='text/plain')
return werkzeug.utils.redirect(url)
@ -240,11 +251,16 @@ class Website(openerp.addons.web.controllers.main.Home):
def publish(self, id, object):
_id = int(id)
_object = request.registry[object]
obj = _object.browse(request.cr, request.uid, _id)
values = {}
if 'website_published' in _object._all_columns:
values['website_published'] = not obj.website_published
if 'website_published_datetime' in _object._all_columns and values.get('website_published'):
values['website_published_datetime'] = fields.datetime.now()
_object.write(request.cr, request.uid, [_id],
{'website_published': not obj.website_published},
context=request.context)
values, context=request.context)
obj = _object.browse(request.cr, request.uid, _id)
return obj.website_published and True or False

View File

@ -39,14 +39,19 @@
<field name="url">/</field>
<field name="target">self</field>
</record>
<record id="action_website_tutorial" model="ir.actions.act_url">
<field name="name">Website With Tutorial</field>
<field name="url">/?tutorial=true</field>
<field name="target">self</field>
</record>
<record id="menu_website" model="ir.ui.menu">
<field name="name">Website</field>
<field name="sequence" eval="510"/>
<field name="action" ref="action_website"/>
</record>
<record id="base.open_menu" model="ir.actions.todo">
<field name="action_id" ref="action_website"/>
<field name="action_id" ref="action_website_tutorial"/>
<field name="state">open</field>
</record>

View File

@ -1,7 +1,9 @@
# -*- coding: utf-8 -*-
import view
import ir_fields
import ir_ui_view
import website
import ir_rule
import ir_qweb
import test_models

View File

@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
from openerp.osv import osv
from openerp.addons.web.http import request
from openerp.tools.misc import unquote as unquote
class ir_rule(osv.osv):
_inherit = 'ir.rule'
def _eval_context_for_combinations(self):
"""Returns a dictionary to use as evaluation context for
ir.rule domains, when the goal is to obtain python lists
that are easier to parse and combine, but not to
actually execute them."""
res = super(ir_rule, self)._eval_context_for_combinations()
res.update(session=unquote('session'))
return res
def _eval_context(self, cr, uid):
"""Returns a dictionary to use as evaluation context for
ir.rule domains."""
res = super(ir_rule, self)._eval_context(cr, uid)
res.update(session=request and request.httprequest and request.httprequest.session)
return res

View File

@ -66,10 +66,12 @@ def url_for(path, lang=None):
def urlplus(url, params):
if not params:
return url
url += "?"
for k,v in params.items():
url += "%s=%s&" % (k, urllib.quote_plus(str(v)))
return url
# can't use urlencode because it encodes to (ascii, replace) in p2
return "%s?%s" % (url, '&'.join(
k + '=' + urllib.quote_plus(v.encode('utf-8') if isinstance(v, unicode) else str(v))
for k, v in params.iteritems()
))
class website(osv.osv):
_name = "website" # Avoid website.website convention for conciseness (for new api). Got a special authorization from xmo and rco
@ -118,6 +120,7 @@ class website(osv.osv):
is_master_lang = lang == request.website.default_lang_id.code
request.context.update({
'lang': lang,
'lang_selected': [lg for lg in request.website.language_ids if lg.code == lang],
'langs': [lg.code for lg in request.website.language_ids],
'multilang': request.multilang,
'is_public_user': is_public_user,
@ -141,13 +144,12 @@ class website(osv.osv):
qweb_context.update(values)
qweb_context.update(
request=request,
registry=request.registry,
request=request, # TODO maybe rename to _request to mark this attribute as unsafe
json=simplejson,
website=request.website,
url_for=url_for,
res_company=request.website.company_id,
user_id=user.browse(cr, openerp.SUPERUSER_ID, uid),
user_id=user.browse(cr, uid, uid),
)
context = request.context.copy()
@ -347,3 +349,28 @@ class res_partner(osv.osv):
'q': '%s, %s %s, %s' % (partner.street, partner.city, partner.zip, partner.country_id and partner.country_id.name_get()[0][1] or ''),
}
return urlplus('https://maps.google.be/maps' , params)
class base_language_install(osv.osv):
_inherit = "base.language.install"
_columns = {
'website_ids': fields.many2many('website', string='Websites to translate'),
}
def lang_install(self, cr, uid, ids, context=None):
if context is None:
context = {}
action = super(base_language_install, self).lang_install(cr, uid, ids, context)
language_obj = self.browse(cr, uid, ids)[0]
website_ids = [website.id for website in language_obj['website_ids']]
lang_id = self.pool['res.lang'].search(cr, uid, [('code', '=', language_obj['lang'])])
if website_ids and lang_id:
data = {'language_ids': [(4, lang_id[0])]}
self.pool['website'].write(cr, uid, website_ids, data)
params = context.get('params', {})
if 'url_return' in params:
return {
'url': params['url_return'].replace('[lang]', language_obj['lang']),
'type': 'ir.actions.act_url',
'target': 'self'
}
return action

View File

@ -0,0 +1,43 @@
.tour-backdrop {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 1009;
background-color: #000;
opacity: 0.8;
}
.tour-step-backdrop {
position: relative;
z-index: 1011;
}
.tour-step-background {
position: absolute;
z-index: 1010;
background: #fff;
border-radius: 6px;
}
.popover[class*="tour-"] .popover-navigation {
padding: 9px 14px;
}
.popover[class*="tour-"] .popover-navigation *[data-role=end] {
float: right;
}
.popover[class*="tour-"] .popover-navigation *[data-role=prev],
.popover[class*="tour-"] .popover-navigation *[data-role=next],
.popover[class*="tour-"] .popover-navigation *[data-role=end] {
cursor: pointer;
}
.popover[class*="tour-"] .popover-navigation *[data-role=prev].disabled,
.popover[class*="tour-"] .popover-navigation *[data-role=next].disabled,
.popover[class*="tour-"] .popover-navigation *[data-role=end].disabled {
cursor: default;
}
.popover[class*="tour-"].orphan {
position: fixed;
margin-top: 0;
}
.popover[class*="tour-"].orphan .arrow {
display: none;
}

View File

@ -0,0 +1,559 @@
/* ===========================================================
# bootstrap-tour - v0.6.1
# http://bootstraptour.com
# ==============================================================
# Copyright 2012-2013 Ulrich Sossou
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
*/
(function() {
(function($, window) {
var Tour, document;
document = window.document;
Tour = (function() {
function Tour(options) {
this._options = $.extend({
name: "tour",
container: "body",
keyboard: true,
storage: window.localStorage,
debug: false,
backdrop: false,
redirect: true,
orphan: false,
basePath: "",
template: "<div class='popover'> <div class='arrow'></div> <h3 class='popover-title'></h3> <div class='popover-content'></div> <nav class='popover-navigation'> <div class='btn-group'> <button class='btn btn-sm btn-default' data-role='prev'>&laquo; Prev</button> <button class='btn btn-sm btn-default' data-role='next'>Next &raquo;</button> </div> <button class='btn btn-sm btn-default' data-role='end'>End tour</button> </nav> </div>",
afterSetState: function(key, value) {},
afterGetState: function(key, value) {},
afterRemoveState: function(key) {},
onStart: function(tour) {},
onEnd: function(tour) {},
onShow: function(tour) {},
onShown: function(tour) {},
onHide: function(tour) {},
onHidden: function(tour) {},
onNext: function(tour) {},
onPrev: function(tour) {}
}, options);
this._steps = [];
this.setCurrentStep();
this.backdrop = {
overlay: null,
$element: null,
$background: null
};
}
Tour.prototype.setState = function(key, value) {
var keyName;
if (this._options.storage) {
keyName = "" + this._options.name + "_" + key;
this._options.storage.setItem(keyName, value);
return this._options.afterSetState(keyName, value);
} else {
if (this._state == null) {
this._state = {};
}
return this._state[key] = value;
}
};
Tour.prototype.removeState = function(key) {
var keyName;
if (this._options.storage) {
keyName = "" + this._options.name + "_" + key;
this._options.storage.removeItem(keyName);
return this._options.afterRemoveState(keyName);
} else {
if (this._state != null) {
return delete this._state[key];
}
}
};
Tour.prototype.getState = function(key) {
var keyName, value;
if (this._options.storage) {
keyName = "" + this._options.name + "_" + key;
value = this._options.storage.getItem(keyName);
} else {
if (this._state != null) {
value = this._state[key];
}
}
if (value === void 0 || value === "null") {
value = null;
}
this._options.afterGetState(key, value);
return value;
};
Tour.prototype.addSteps = function(steps) {
var step, _i, _len, _results;
_results = [];
for (_i = 0, _len = steps.length; _i < _len; _i++) {
step = steps[_i];
_results.push(this.addStep(step));
}
return _results;
};
Tour.prototype.addStep = function(step) {
return this._steps.push(step);
};
Tour.prototype.getStep = function(i) {
if (this._steps[i] != null) {
return $.extend({
id: "step-" + i,
path: "",
placement: "right",
title: "",
content: "<p></p>",
next: i === this._steps.length - 1 ? -1 : i + 1,
prev: i - 1,
animation: true,
container: this._options.container,
backdrop: this._options.backdrop,
redirect: this._options.redirect,
orphan: this._options.orphan,
template: this._options.template,
onShow: this._options.onShow,
onShown: this._options.onShown,
onHide: this._options.onHide,
onHidden: this._options.onHidden,
onNext: this._options.onNext,
onPrev: this._options.onPrev
}, this._steps[i]);
}
};
Tour.prototype.start = function(force) {
var promise,
_this = this;
if (force == null) {
force = false;
}
if (this.ended() && !force) {
return this._debug("Tour ended, start prevented.");
}
$(document).off("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role=next]").on("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role=next]:not(.disabled)", function(e) {
e.preventDefault();
return _this.next();
});
$(document).off("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role=prev]").on("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role=prev]:not(.disabled)", function(e) {
e.preventDefault();
return _this.prev();
});
$(document).off("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role=end]").on("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role=end]", function(e) {
e.preventDefault();
return _this.end();
});
this._onResize(function() {
return _this.showStep(_this._current);
});
this._setupKeyboardNavigation();
promise = this._makePromise(this._options.onStart != null ? this._options.onStart(this) : void 0);
return this._callOnPromiseDone(promise, this.showStep, this._current);
};
Tour.prototype.next = function() {
var promise;
if (this.ended()) {
return this._debug("Tour ended, next prevented.");
}
promise = this.hideStep(this._current);
return this._callOnPromiseDone(promise, this._showNextStep);
};
Tour.prototype.prev = function() {
var promise;
if (this.ended()) {
return this._debug("Tour ended, prev prevented.");
}
promise = this.hideStep(this._current);
return this._callOnPromiseDone(promise, this._showPrevStep);
};
Tour.prototype.goto = function(i) {
var promise;
if (this.ended()) {
return this._debug("Tour ended, goto prevented.");
}
promise = this.hideStep(this._current);
return this._callOnPromiseDone(promise, this.showStep, i);
};
Tour.prototype.end = function() {
var endHelper, hidePromise,
_this = this;
endHelper = function(e) {
$(document).off("click.tour-" + _this._options.name);
$(document).off("keyup.tour-" + _this._options.name);
$(window).off("resize.tour-" + _this._options.name);
_this.setState("end", "yes");
if (_this._options.onEnd != null) {
return _this._options.onEnd(_this);
}
};
hidePromise = this.hideStep(this._current);
return this._callOnPromiseDone(hidePromise, endHelper);
};
Tour.prototype.ended = function() {
return !!this.getState("end");
};
Tour.prototype.restart = function() {
this.removeState("current_step");
this.removeState("end");
this.setCurrentStep(0);
return this.start();
};
Tour.prototype.hideStep = function(i) {
var hideStepHelper, promise, step,
_this = this;
step = this.getStep(i);
promise = this._makePromise(step.onHide != null ? step.onHide(this, i) : void 0);
hideStepHelper = function(e) {
var $element;
$element = _this._isOrphan(step) ? $("body") : $(step.element);
$element.popover("destroy");
if (step.reflex) {
$element.css("cursor", "").off("click.tour-" + _this._options.name);
}
if (step.backdrop) {
_this._hideBackdrop();
}
if (step.onHidden != null) {
return step.onHidden(_this);
}
};
this._callOnPromiseDone(promise, hideStepHelper);
return promise;
};
Tour.prototype.showStep = function(i) {
var promise, showStepHelper, skipToPrevious, step,
_this = this;
step = this.getStep(i);
if (!step) {
return;
}
skipToPrevious = i < this._current;
promise = this._makePromise(step.onShow != null ? step.onShow(this, i) : void 0);
showStepHelper = function(e) {
var current_path, path;
_this.setCurrentStep(i);
path = $.isFunction(step.path) ? step.path.call() : _this._options.basePath + step.path;
current_path = [document.location.pathname, document.location.hash].join("");
if (_this._isRedirect(path, current_path)) {
_this._redirect(step, path);
return;
}
if (_this._isOrphan(step)) {
if (!step.orphan) {
_this._debug("Skip the orphan step " + (_this._current + 1) + ". Orphan option is false and the element doesn't exist or is hidden.");
if (skipToPrevious) {
_this._showPrevStep();
} else {
_this._showNextStep();
}
return;
}
_this._debug("Show the orphan step " + (_this._current + 1) + ". Orphans option is true.");
}
if (step.backdrop) {
_this._showBackdrop(!_this._isOrphan(step) ? step.element : void 0);
}
_this._showPopover(step, i);
if (step.onShown != null) {
step.onShown(_this);
}
return _this._debug("Step " + (_this._current + 1) + " of " + _this._steps.length);
};
return this._callOnPromiseDone(promise, showStepHelper);
};
Tour.prototype.setCurrentStep = function(value) {
if (value != null) {
this._current = value;
return this.setState("current_step", value);
} else {
this._current = this.getState("current_step");
return this._current = this._current === null ? 0 : parseInt(this._current, 10);
}
};
Tour.prototype._showNextStep = function() {
var promise, showNextStepHelper, step,
_this = this;
step = this.getStep(this._current);
showNextStepHelper = function(e) {
return _this.showStep(step.next);
};
promise = this._makePromise((step.onNext != null ? step.onNext(this) : void 0));
return this._callOnPromiseDone(promise, showNextStepHelper);
};
Tour.prototype._showPrevStep = function() {
var promise, showPrevStepHelper, step,
_this = this;
step = this.getStep(this._current);
showPrevStepHelper = function(e) {
return _this.showStep(step.prev);
};
promise = this._makePromise((step.onPrev != null ? step.onPrev(this) : void 0));
return this._callOnPromiseDone(promise, showPrevStepHelper);
};
Tour.prototype._debug = function(text) {
if (this._options.debug) {
return window.console.log("Bootstrap Tour '" + this._options.name + "' | " + text);
}
};
Tour.prototype._isRedirect = function(path, currentPath) {
return (path != null) && path !== "" && path.replace(/\?.*$/, "").replace(/\/?$/, "") !== currentPath.replace(/\/?$/, "");
};
Tour.prototype._redirect = function(step, path) {
if ($.isFunction(step.redirect)) {
return step.redirect.call(this, path);
} else if (step.redirect === true) {
this._debug("Redirect to " + path);
return document.location.href = path;
}
};
Tour.prototype._isOrphan = function(step) {
return (step.element == null) || !$(step.element).length || $(step.element).is(":hidden");
};
Tour.prototype._showPopover = function(step, i) {
var $element, $navigation, $template, $tip, isOrphan, options,
_this = this;
options = $.extend({}, this._options);
$template = $.isFunction(step.template) ? $(step.template(i, step)) : $(step.template);
$navigation = $template.find(".popover-navigation");
isOrphan = this._isOrphan(step);
if (isOrphan) {
step.element = "body";
step.placement = "top";
$template = $template.addClass("orphan");
}
$element = $(step.element);
$template.addClass("tour-" + this._options.name);
if (step.options) {
$.extend(options, step.options);
}
if (step.reflex) {
$element.css("cursor", "pointer").on("click.tour-" + this._options.name, function(e) {
if (_this._current < _this._steps.length - 1) {
return _this.next();
} else {
return _this.end();
}
});
}
if (step.prev < 0) {
$navigation.find("*[data-role=prev]").addClass("disabled");
}
if (step.next < 0) {
$navigation.find("*[data-role=next]").addClass("disabled");
}
step.template = $template.clone().wrap("<div>").parent().html();
$element.popover({
placement: step.placement,
trigger: "manual",
title: step.title,
content: step.content,
html: true,
animation: step.animation,
container: step.container,
template: step.template,
selector: step.element
}).popover("show");
$tip = $element.data("bs.popover") ? $element.data("bs.popover").tip() : $element.data("popover").tip();
$tip.attr("id", step.id);
this._scrollIntoView($tip);
this._reposition($tip, step);
if (isOrphan) {
return this._center($tip);
}
};
Tour.prototype._reposition = function($tip, step) {
var offsetBottom, offsetHeight, offsetRight, offsetWidth, originalLeft, originalTop, tipOffset;
offsetWidth = $tip[0].offsetWidth;
offsetHeight = $tip[0].offsetHeight;
tipOffset = $tip.offset();
originalLeft = tipOffset.left;
originalTop = tipOffset.top;
offsetBottom = $(document).outerHeight() - tipOffset.top - $tip.outerHeight();
if (offsetBottom < 0) {
tipOffset.top = tipOffset.top + offsetBottom;
}
offsetRight = $("html").outerWidth() - tipOffset.left - $tip.outerWidth();
if (offsetRight < 0) {
tipOffset.left = tipOffset.left + offsetRight;
}
if (tipOffset.top < 0) {
tipOffset.top = 0;
}
if (tipOffset.left < 0) {
tipOffset.left = 0;
}
$tip.offset(tipOffset);
if (step.placement === "bottom" || step.placement === "top") {
if (originalLeft !== tipOffset.left) {
return this._replaceArrow($tip, (tipOffset.left - originalLeft) * 2, offsetWidth, "left");
}
} else {
if (originalTop !== tipOffset.top) {
return this._replaceArrow($tip, (tipOffset.top - originalTop) * 2, offsetHeight, "top");
}
}
};
Tour.prototype._center = function($tip) {
return $tip.css("top", $(window).outerHeight() / 2 - $tip.outerHeight() / 2);
};
Tour.prototype._replaceArrow = function($tip, delta, dimension, position) {
return $tip.find(".arrow").css(position, delta ? 50 * (1 - delta / dimension) + "%" : "");
};
Tour.prototype._scrollIntoView = function(tip) {
return $("html, body").stop().animate({
scrollTop: Math.ceil(tip.offset().top - ($(window).height() / 2))
});
};
Tour.prototype._onResize = function(callback, timeout) {
return $(window).on("resize.tour-" + this._options.name, function() {
clearTimeout(timeout);
return timeout = setTimeout(callback, 100);
});
};
Tour.prototype._setupKeyboardNavigation = function() {
var _this = this;
if (this._options.keyboard) {
return $(document).on("keyup.tour-" + this._options.name, function(e) {
if (!e.which) {
return;
}
switch (e.which) {
case 39:
e.preventDefault();
if (_this._current < _this._steps.length - 1) {
return _this.next();
} else {
return _this.end();
}
break;
case 37:
e.preventDefault();
if (_this._current > 0) {
return _this.prev();
}
break;
case 27:
e.preventDefault();
return _this.end();
}
});
}
};
Tour.prototype._makePromise = function(result) {
if (result && $.isFunction(result.then)) {
return result;
} else {
return null;
}
};
Tour.prototype._callOnPromiseDone = function(promise, cb, arg) {
var _this = this;
if (promise) {
return promise.then(function(e) {
return cb.call(_this, arg);
});
} else {
return cb.call(this, arg);
}
};
Tour.prototype._showBackdrop = function(element) {
if (this.backdrop.overlay !== null) {
return;
}
this._showOverlay();
if (element != null) {
return this._showOverlayElement(element);
}
};
Tour.prototype._hideBackdrop = function() {
if (this.backdrop.overlay === null) {
return;
}
if (this.backdrop.$element) {
this._hideOverlayElement();
}
return this._hideOverlay();
};
Tour.prototype._showOverlay = function() {
this.backdrop = $("<div/>", {
"class": "tour-backdrop"
});
return $("body").append(this.backdrop);
};
Tour.prototype._hideOverlay = function() {
this.backdrop.remove();
return this.backdrop.overlay = null;
};
Tour.prototype._showOverlayElement = function(element) {
var $background, $element, offset;
$element = $(element);
$background = $("<div/>");
offset = $element.offset();
offset.top = offset.top;
offset.left = offset.left;
$background.width($element.innerWidth()).height($element.innerHeight()).addClass("tour-step-background").offset(offset);
$element.addClass("tour-step-backdrop");
$("body").append($background);
this.backdrop.$element = $element;
return this.backdrop.$background = $background;
};
Tour.prototype._hideOverlayElement = function() {
this.backdrop.$element.removeClass("tour-step-backdrop");
this.backdrop.$background.remove();
this.backdrop.$element = null;
return this.backdrop.$background = null;
};
return Tour;
})();
return window.Tour = Tour;
})(jQuery, window);
}).call(this);

View File

@ -0,0 +1,22 @@
jQuery Nearest Element plugin
======
**Full documentation is at <http://gilmoreorless.github.io/jquery-nearest/>**
**Demo:** <http://gilmoreorless.github.io/jquery-nearest/demo.html>
Method signatures:
* `$.nearest({x, y}, selector)` - find `$(selector)` closest to x/y point on screen
* `$(elem).nearest(selector)` - find `$(selector)` closest to elem
* `$(elemSet).nearest({x, y})` - filter `$(elemSet)` and return closest to x/y point on screen
Reverse logic:
* `$.furthest()`
* `$(elem).furthest()`
Intersecting/touching:
* `$.touching()`
* `$(elem).touching()`

View File

@ -0,0 +1,12 @@
{
"name": "jquery-nearest",
"version": "1.2.1",
"main": "jquery.nearest.js",
"ignore": [
"**/.*",
"test"
],
"dependencies": {
"jquery": ">=1.4"
}
}

View File

@ -0,0 +1,223 @@
/*!
* jQuery Nearest plugin v1.2.1
*
* Finds elements closest to a single point based on screen location and pixel dimensions
* http://gilmoreorless.github.com/jquery-nearest/
* Open source under the MIT licence: http://gilmoreorless.mit-license.org/2011/
*
* Requires jQuery 1.4 or above
* Also supports Ben Alman's "each2" plugin for faster looping (if available)
*/
/**
* Method signatures:
*
* $.nearest({x, y}, selector) - find $(selector) closest to point
* $(elem).nearest(selector) - find $(selector) closest to elem
* $(elemSet).nearest({x, y}) - filter $(elemSet) and return closest to point
*
* Also:
* $.furthest()
* $(elem).furthest()
*
* $.touching()
* $(elem).touching()
*/
;(function ($, undefined) {
/**
* Internal method that does the grunt work
*
* @param mixed selector Any valid jQuery selector providing elements to filter
* @param hash options Key/value list of options for matching elements
* @param mixed thisObj (optional) Any valid jQuery selector that represents self
* for the "includeSelf" option
* @return array List of matching elements, can be zero length
*/
var rPerc = /^([\d.]+)%$/;
function nearest(selector, options, thisObj) {
// Normalise selector and dimensions
selector || (selector = 'div'); // I STRONGLY recommend passing in a selector
var $container = $(options.container),
containerOffset = $container.offset() || {left: 0, top: 0},
containerDims = [
containerOffset.left + $container.width(),
containerOffset.top + $container.height()
],
percProps = {x: 0, y: 1, w: 0, h: 1},
prop, match;
for (prop in percProps) if (percProps.hasOwnProperty(prop)) {
match = rPerc.exec(options[prop]);
if (match) {
options[prop] = containerDims[percProps[prop]] * match[1] / 100;
}
}
// Get elements and work out x/y points
var $all = $(selector),
cache = [],
furthest = !!options.furthest,
checkX = !!options.checkHoriz,
checkY = !!options.checkVert,
compDist = furthest ? 0 : Infinity,
point1x = parseFloat(options.x) || 0,
point1y = parseFloat(options.y) || 0,
point2x = parseFloat(point1x + options.w) || point1x,
point2y = parseFloat(point1y + options.h) || point1y,
tolerance = options.tolerance || 0,
hasEach2 = !!$.fn.each2,
// Shortcuts to help with compression
min = Math.min,
max = Math.max;
// Normalise the remaining options
if (!options.includeSelf && thisObj) {
$all = $all.not(thisObj);
}
if (tolerance < 0) {
tolerance = 0;
}
// Loop through all elements and check their positions
$all[hasEach2 ? 'each2' : 'each'](function (i, elem) {
var $this = hasEach2 ? elem : $(this),
off = $this.offset(),
x = off.left,
y = off.top,
w = $this.outerWidth(),
h = $this.outerHeight(),
x2 = x + w,
y2 = y + h,
maxX1 = max(x, point1x),
minX2 = min(x2, point2x),
maxY1 = max(y, point1y),
minY2 = min(y2, point2y),
intersectX = minX2 >= maxX1,
intersectY = minY2 >= maxY1,
distX, distY, distT, isValid;
if (
// .nearest() / .furthest()
(checkX && checkY) ||
// .touching()
(!checkX && !checkY && intersectX && intersectY) ||
// .nearest({checkVert: false})
(checkX && intersectY) ||
// .nearest({checkHoriz: false})
(checkY && intersectX)
) {
distX = intersectX ? 0 : maxX1 - minX2;
distY = intersectY ? 0 : maxY1 - minY2;
distT = intersectX || intersectY ?
max(distX, distY) :
Math.sqrt(distX * distX + distY * distY);
isValid = furthest ?
distT >= compDist - tolerance :
distT <= compDist + tolerance;
if (isValid) {
compDist = furthest ?
max(compDist, distT) :
min(compDist, distT);
cache.push({
node: this,
dist: distT
});
}
}
});
// Make sure all cached items are within tolerance range
var len = cache.length,
filtered = [],
compMin, compMax,
i, item;
if (len) {
if (furthest) {
compMin = compDist - tolerance;
compMax = compDist;
} else {
compMin = compDist;
compMax = compDist + tolerance;
}
for (i = 0; i < len; i++) {
item = cache[i];
if (item.dist >= compMin && item.dist <= compMax) {
filtered.push(item.node);
}
}
}
return filtered;
}
$.each(['nearest', 'furthest', 'touching'], function (i, name) {
// Internal default options
// Not exposed publicly because they're method-dependent and easily overwritten anyway
var defaults = {
x: 0, // X position of top left corner of point/region
y: 0, // Y position of top left corner of point/region
w: 0, // Width of region
h: 0, // Height of region
tolerance: 1, // Distance tolerance in pixels, mainly to handle fractional pixel rounding bugs
container: document, // Container of objects for calculating %-based dimensions
furthest: name == 'furthest', // Find max distance (true) or min distance (false)
includeSelf: false, // Include 'this' in search results (t/f) - only applies to $(elem).func(selector) syntax
checkHoriz: name != 'touching', // Check variations in X axis (t/f)
checkVert: name != 'touching' // Check variations in Y axis (t/f)
};
/**
* $.nearest() / $.furthest() / $.touching()
*
* Utility functions for finding elements near a specific point or region on screen
*
* @param hash point Co-ordinates for the point or region to measure from
* "x" and "y" keys are required, "w" and "h" keys are optional
* @param mixed selector Any valid jQuery selector that provides elements to filter
* @param hash options (optional) Extra filtering options
* Not technically needed as the options could go on the point object,
* but it's good to have a consistent API
* @return jQuery object containing matching elements in selector
*/
$[name] = function (point, selector, options) {
if (!point || point.x === undefined || point.y === undefined) {
return $([]);
}
var opts = $.extend({}, defaults, point, options || {});
return $(nearest(selector, opts));
};
/**
* SIGNATURE 1:
* $(elem).nearest(selector) / $(elem).furthest(selector) / $(elem).touching(selector)
*
* Finds all elements in selector that are nearest to/furthest from elem
*
* @param mixed selector Any valid jQuery selector that provides elements to filter
* @param hash options (optional) Extra filtering options
* @return jQuery object containing matching elements in selector
*
* SIGNATURE 2:
* $(elemSet).nearest(point) / $(elemSet).furthest(point) / $(elemSet).touching(point)
*
* Filters elemSet to return only the elements nearest to/furthest from point
* Effectively a wrapper for $.nearest(point, elemSet) but with the benefits of method chaining
*
* @param hash point Co-ordinates for the point or region to measure from
* @return jQuery object containing matching elements in elemSet
*/
$.fn[name] = function (selector, options) {
var opts;
if (selector && $.isPlainObject(selector)) {
opts = $.extend({}, defaults, selector, options || {});
return this.pushStack(nearest(this, opts));
}
var offset = this.offset(),
dimensions = {
x: offset.left,
y: offset.top,
w: this.outerWidth(),
h: this.outerHeight()
};
opts = $.extend({}, defaults, dimensions, options || {});
return this.pushStack(nearest(selector, opts, this));
};
});
})(jQuery);

View File

@ -0,0 +1,11 @@
/*!
* jQuery Nearest plugin v1.2.1
*
* Finds elements closest to a single point based on screen location and pixel dimensions
* http://gilmoreorless.github.com/jquery-nearest/
* Open source under the MIT licence: http://gilmoreorless.mit-license.org/2011/
*
* Requires jQuery 1.4 or above
* Also supports Ben Alman's "each2" plugin for faster looping (if available)
*/
;(function(e,t){function r(t,r,i){t||(t="div");var s=e(r.container),o=s.offset()||{left:0,top:0},u=[o.left+s.width(),o.top+s.height()],a={x:0,y:1,w:0,h:1},f,l;for(f in a)a.hasOwnProperty(f)&&(l=n.exec(r[f]),l&&(r[f]=u[a[f]]*l[1]/100));var c=e(t),h=[],p=!!r.furthest,d=!!r.checkHoriz,v=!!r.checkVert,m=p?0:Infinity,g=parseFloat(r.x)||0,y=parseFloat(r.y)||0,b=parseFloat(g+r.w)||g,w=parseFloat(y+r.h)||y,E=r.tolerance||0,S=!!e.fn.each2,x=Math.min,T=Math.max;!r.includeSelf&&i&&(c=c.not(i)),E<0&&(E=0),c[S?"each2":"each"](function(t,n){var r=S?n:e(this),i=r.offset(),s=i.left,o=i.top,u=r.outerWidth(),a=r.outerHeight(),f=s+u,l=o+a,c=T(s,g),N=x(f,b),C=T(o,y),k=x(l,w),L=N>=c,A=k>=C,O,M,_,D;if(d&&v||!d&&!v&&L&&A||d&&A||v&&L)O=L?0:c-N,M=A?0:C-k,_=L||A?T(O,M):Math.sqrt(O*O+M*M),D=p?_>=m-E:_<=m+E,D&&(m=p?T(m,_):x(m,_),h.push({node:this,dist:_}))});var N=h.length,C=[],k,L,A,O;if(N){p?(k=m-E,L=m):(k=m,L=m+E);for(A=0;A<N;A++)O=h[A],O.dist>=k&&O.dist<=L&&C.push(O.node)}return C}var n=/^([\d.]+)%$/;e.each(["nearest","furthest","touching"],function(n,i){var s={x:0,y:0,w:0,h:0,tolerance:1,container:document,furthest:i=="furthest",includeSelf:!1,checkHoriz:i!="touching",checkVert:i!="touching"};e[i]=function(n,i,o){if(!n||n.x===t||n.y===t)return e([]);var u=e.extend({},s,n,o||{});return e(r(i,u))},e.fn[i]=function(t,n){var i;if(t&&e.isPlainObject(t))return i=e.extend({},s,t,n||{}),this.pushStack(r(this,i));var o=this.offset(),u={x:o.left,y:o.top,w:this.outerWidth(),h:this.outerHeight()};return i=e.extend({},s,u,n||{}),this.pushStack(r(t,i,this))}})})(jQuery);

View File

@ -0,0 +1,24 @@
{
"name": "nearest",
"title": "jQuery Nearest Element",
"description": "Find the elements in a page that are closest to (or furthest away from) a particular point or element, based on pixel dimensions",
"version": "1.2.1",
"keywords": ["nearest", "furthest", "touching", "pixel", "distance"],
"homepage": "http://gilmoreorless.github.io/jquery-nearest/",
"docs": "http://gilmoreorless.github.io/jquery-nearest/",
"demo": "http://gilmoreorless.github.io/jquery-nearest/demo.html",
"bugs": "https://github.com/gilmoreorless/jquery-nearest/issues",
"author": {
"name": "Gilmore Davidson",
"url": "http://shoehornwithteeth.com/"
},
"licenses": [
{
"type": "MIT",
"url": "http://gilmoreorless.mit-license.org/2011/"
}
],
"dependencies": {
"jquery": ">=1.4"
}
}

View File

@ -1,4 +1,3 @@
@charset "utf-8";
/* ---- CKEditor Minimal Reset ---- */
.navbar.navbar-inverse .cke_chrome {
border: none;
@ -169,7 +168,7 @@ table.editorbar-panel td.selected {
position: fixed;
left: 0px;
right: 0px;
top: 50px;
top: 51px;
background: #282828;
-webkit-box-shadow: 0px 10px 10px -10px black inset;
-moz-box-shadow: 0px 10px 10px -10px black inset;
@ -182,17 +181,21 @@ table.editorbar-panel td.selected {
}
#oe_snippets .scroll {
white-space: nowrap;
overflow-y: auto;
overflow-y: none;
}
#oe_snippets .nav {
position: fixed;
display: inline-block;
border-bottom: none !important;
vertical-align: middle;
min-width: 120px;
z-index: 1;
border: 0;
}
#oe_snippets .nav > li {
display: inline-block;
display: block;
float: none;
font-size: 13px;
}
#oe_snippets .nav > li.active {
background: black !important;
}
#oe_snippets .nav > li > a {
padding: 2px 10px !important;
@ -200,18 +203,19 @@ table.editorbar-panel td.selected {
display: block;
border: 0;
}
#oe_snippets .pill-content {
border: 0;
}
#oe_snippets .tab-content {
margin-top: 24px;
display: inline-block;
white-space: nowrap;
background: black;
}
#oe_snippets .tab-content > div {
background: black;
padding: 5px;
}
#oe_snippets .tab-content > div label {
width: 44px;
height: 100%;
color: white;
padding-left: 10px;
}
@ -231,11 +235,10 @@ table.editorbar-panel td.selected {
}
.oe_snippet {
display: inline-block;
float: left;
vertical-align: top;
width: 100px;
height: 100px;
margin: 1px;
width: 93px;
margin-left: 1px;
margin-top: 0px;
position: relative;
overflow: hidden;
@ -253,14 +256,32 @@ table.editorbar-panel td.selected {
color: white;
position: relative;
}
.oe_snippet .oe_snippet_thumbnail:hover .oe_snippet_thumbnail_img {
-webkit-transform: scale(0.95, 0.95);
-moz-transform: scale(0.95, 0.95);
-ms-transform: scale(0.95, 0.95);
-o-transform: scale(0.95, 0.95);
transform: scale(0.95, 0.95);
}
.oe_snippet .oe_snippet_thumbnail .oe_snippet_thumbnail_title {
font-size: 12px;
display: block;
text-shadow: 0 0 2px black;
}
.oe_snippet .oe_snippet_thumbnail .oe_snippet_thumbnail_img {
height: 74px;
-webkit-transition: all 150ms linear;
-moz-transition: all 150ms linear;
-o-transition: all 150ms linear;
transition: all 150ms linear;
-webkit-box-shadow: inset 0px 0px 0px 3px #333333;
-moz-box-shadow: inset 0px 0px 0px 3px #333333;
box-shadow: inset 0px 0px 0px 3px #333333;
-webkit-transform: scale(1, 1);
-moz-transform: scale(1, 1);
-ms-transform: scale(1, 1);
-o-transform: scale(1, 1);
transform: scale(1, 1);
}
.oe_snippet .oe_snippet_thumbnail span, .oe_snippet .oe_snippet_thumbnail div {
line-height: 18px;
@ -309,20 +330,21 @@ table.editorbar-panel td.selected {
margin: 0px -24px !important;
}
.oe_drop_zone.oe_insert.oe_overlay {
background: rgba(153, 0, 255, 0.17);
border-radius: 3px;
background: rgba(153, 0, 255, 0.5);
}
.oe_drop_zone, .oe_drop_zone_style {
background: rgba(153, 0, 255, 0.17);
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
-ms-border-radius: 3px;
-o-border-radius: 3px;
border-radius: 3px;
border: none;
background: rgba(153, 0, 255, 0.3);
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
-ms-border-radius: 4px;
-o-border-radius: 4px;
border-radius: 4px;
}
.oe_drop_zone.oe_hover, .oe_drop_zone_style.oe_hover {
background: rgba(0, 255, 133, 0.22);
background: rgba(0, 255, 133, 0.3);
z-index: 1001;
}
@ -359,7 +381,7 @@ table.editorbar-panel td.selected {
-webkit-box-shadow: 0px 0px 0px 1px rgba(255, 255, 255, 0.3), 0px 0px 0px 1px rgba(255, 255, 255, 0.3) inset;
-moz-box-shadow: 0px 0px 0px 1px rgba(255, 255, 255, 0.3), 0px 0px 0px 1px rgba(255, 255, 255, 0.3) inset;
box-shadow: 0px 0px 0px 1px rgba(255, 255, 255, 0.3), 0px 0px 0px 1px rgba(255, 255, 255, 0.3) inset;
border-color: rgba(153, 0, 255, 0.5);
border-color: rgba(0, 0, 0, 0.5);
}
.oe_overlay.oe_active .oe_handle {
display: block !important;
@ -371,39 +393,43 @@ table.editorbar-panel td.selected {
left: 50%;
display: block;
background: white;
border: solid 1px #9900ff;
-webkit-border-radius: 8px;
-moz-border-radius: 8px;
-ms-border-radius: 8px;
-o-border-radius: 8px;
border-radius: 8px;
width: 16px;
height: 16px;
margin: -8px;
border: solid 1px rgba(0, 0, 0, 0.2);
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
-ms-border-radius: 5px;
-o-border-radius: 5px;
border-radius: 5px;
width: 18px;
height: 18px;
margin: -9px;
padding-left: 1px;
font-size: 14px;
line-height: 14px;
color: #9900ff;
font-family: FontAwesome;
color: rgba(0, 0, 0, 0.5);
-webkit-transition: background 100ms linear;
-moz-transition: background 100ms linear;
-o-transition: background 100ms linear;
transition: background 100ms linear;
}
.oe_overlay.oe_active .oe_handle:hover:before {
background: #9900ff;
background: rgba(0, 0, 0, 0.5);
color: white;
-webkit-box-shadow: 0 0 5px 3px rgba(255, 255, 255, 0.7);
-moz-box-shadow: 0 0 5px 3px rgba(255, 255, 255, 0.7);
box-shadow: 0 0 5px 3px rgba(255, 255, 255, 0.7);
}
.oe_overlay.oe_active .oe_handle.e:before {
content: "⇨";
content: "\F061";
}
.oe_overlay.oe_active .oe_handle.s:before {
content: "";
content: "\F063";
}
.oe_overlay.oe_active .oe_handle.w:before {
content: "";
content: "\F060";
}
.oe_overlay.oe_active .oe_handle.n:before {
content: "";
content: "\F062";
}
.oe_overlay .icon.btn {
display: inline-block;
@ -568,18 +594,6 @@ table.editorbar-panel td.selected {
-o-border-radius: 0.4em;
border-radius: 0.4em;
}
.oe_seo_configuration .keyword-in-title {
background-color: #5cb85cpointer;
color: white;
}
.oe_seo_configuration .keyword-in-description {
background-color: #428bca;
color: white;
}
.oe_seo_configuration .keyword-in-body {
background-color: #5bc0de;
color: white;
}
/* ---- ACE EDITOR ---- */
.oe_ace_view_editor {
@ -636,3 +650,12 @@ table.editorbar-panel td.selected {
filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0);
opacity: 0;
}
/* ---- EDITOR TOUR ---- */
div.tour-backdrop {
z-index: 2009;
}
.popover.tour {
z-index: 2010;
}

View File

@ -4,6 +4,17 @@
@import "compass/css3/user-interface"
@import "compass/css3/transition"
//smartphones, iPhone, portrait 480x320 phones
$smart_phone: 320px
//portrait e-readers (Nook/Kindle), smaller tablets @ 600 or @ 640 wide.*/
$small-tablet: 481px
//portrait tablets, portrait iPad, landscape e-readers, landscape 800x480 or 854x480 phones
$portrait-tablet: 641px
//tablet, landscape iPad, lo-res laptops ands desktops
$tablet: 961px
//big landscape tablets, laptops, and desktops
$desktop: 1025px
/* ---- CKEditor Minimal Reset ---- */
.navbar.navbar-inverse .cke_chrome
@ -141,7 +152,7 @@ table.editorbar-panel
position: fixed
left: 0px
right: 0px
top: 50px
top: 51px
background: rgb(40,40,40)
+box-shadow(0px 10px 10px -10px black inset)
z-index: 1010
@ -150,44 +161,47 @@ table.editorbar-panel
height: auto
.scroll
white-space: nowrap
overflow-y: auto
overflow-y: none
.nav
display: inline-block
border-bottom: none !important
vertical-align: middle
min-width: 120px
> li
display: inline-block
display: block
float: none
font-size: 13px
&.active
background: black !important
> a
padding: 2px 10px !important
width: 100%
width: 100%
display: block
border: 0
position: fixed
z-index: 1
.pill-content
border: 0
.tab-content
margin-top: 24px
display: inline-block
white-space: nowrap
> div
background: black
> div
background: rgb(0,0,0)
padding: 5px
label
width: 44px
height: 100%
color: #fff
padding-left: 10px
div
width: 100px
text-align: center
@include transform( translate(-39px, 44px) , rotate(-90deg) )
@include transform( translate(-39px, 44px) , rotate(-90deg) )
@include transform-origin(50% 50%)
.oe_snippet
display: inline-block
float: left
vertical-align: top
width: 100px
height: 100px
margin: 1px
width: 93px
margin-left: 1px
margin-top: 0px
position: relative
overflow: hidden
@ -201,11 +215,18 @@ table.editorbar-panel
background: transparent
color: white
position: relative
&:hover
.oe_snippet_thumbnail_img
@include transform( scale(.95,.95))
.oe_snippet_thumbnail_title
font-size: 12px
display: block
+text-shadow(0 0 2px rgb(0,0,0))
.oe_snippet_thumbnail_img
height: 74px
@include transition(all 150ms linear)
+box-shadow(inset 0px 0px 0px 3px #333333)
@include transform( scale(1,1))
span, div
line-height: 18px
& > :not(.oe_snippet_thumbnail)
@ -240,14 +261,19 @@ table.editorbar-panel
position: relative
margin: 0px -24px !important
&.oe_overlay
background: rgba(153, 0, 255, 0.17)
border-radius: 3px
//@include background-image( repeating-linear-gradient(45deg, rgba(255,255,255,.1) ,rgba(255,255,255,.1) 35px, rgba(0,0,0,.1) 35px, rgba(0,0,0,.1) 75px))
//background-size: 100px 100px
background: rgba(153, 0, 255,.5)
.oe_drop_zone, .oe_drop_zone_style
background: rgba(153, 0, 255, 0.17)
+border-radius(3px)
border: none
//@include background-image( repeating-linear-gradient(45deg, rgba(255,255,255,.1) ,rgba(255,255,255,.1) 35px, rgba(0,0,0,.1) 35px, rgba(0,0,0,.1) 75px))
//background-size: 100px 100px
background: rgba(153, 0, 255, .3)
+border-radius(4px)
&.oe_hover
background: rgba(0, 255, 133, 0.22)
background: rgba(0, 255, 133, .3)
z-index: 1001
.oe_drop_zone_style
@ -262,6 +288,7 @@ table.editorbar-panel
display: none
position: absolute
background: transparent
//@include background-image( repeating-linear-gradient(45deg, rgba(255,255,255,.02) ,rgba(255,255,255,.02) 35px, rgba(0,0,0,.02) 35px, rgba(0,0,0,.02) 75px))
+border-radius(3px)
@include transition(opacity 100ms linear)
+box-sizing(border-box)
@ -271,7 +298,7 @@ table.editorbar-panel
border-style: dashed
border-width: 1px
+box-shadow(0px 0px 0px 1px rgba(255,255,255,0.3), 0px 0px 0px 1px rgba(255,255,255,0.3) inset)
border-color: rgba(153, 0, 255, 0.5)
border-color: rgba(0, 0, 0, 0.5)
.oe_handle
display: block !important
pointer-events: auto
@ -281,27 +308,29 @@ table.editorbar-panel
left: 50%
display: block
background: rgba(255, 255, 255, 1)
border: solid 1px rgba(153, 0, 255, 1)
+border-radius(8px)
width: 16px
height: 16px
margin: -8px
border: solid 1px rgba(0, 0, 0, .2)
+border-radius(5px)
width: 18px
height: 18px
margin: -9px
padding-left: 1px
font-size: 14px
line-height: 14px
color: rgba(153, 0, 255, 1)
font-family: FontAwesome
color: rgba(0,0,0,.5)
@include transition(background 100ms linear)
&:hover:before
background: rgba(153, 0, 255, 1)
background: rgba(0, 0, 0, .5)
color: #fff
+box-shadow(0 0 5px 3px rgba(255,255,255,.7))
.oe_handle.e:before
content: ""
content: "\F061"
.oe_handle.s:before
content: ""
content: "\F063"
.oe_handle.w:before
content: ""
content: "\F060"
.oe_handle.n:before
content: ""
content: "\F062"
.icon.btn
display: inline-block
@ -436,10 +465,6 @@ $ace_width: 720px
$editorbar_height: 40px
// TODO Fix => might break with themes
$navbar_height: 51px
@mixin pointer-events($type: none)
$type: unquote($type)
@include experimental(pointer-events, $type,-moz, -webkit, not -o, not -ms, -khtml, official)
@mixin editor-font
font-size: 14px
@ -481,4 +506,10 @@ $navbar_height: 51px
&.oe_ace_closed
width: 0
+opacity(0)
/* ---- EDITOR TOUR ---- */
div.tour-backdrop
z-index: 2009
.popover.tour
z-index: 2010
// vim:tabstop=4:shiftwidth=4:softtabstop=4:fdm=marker:

View File

@ -223,11 +223,15 @@ footer {
}
/* ---- HACK FOR COVERING UP CK EDITOR BOGUS P INSERTION --- */
.oe_structure.oe_empty:empty, [data-oe-type=html]:empty, .oe_structure.oe_empty > .oe_drop_zone.oe_insert:only-child, [data-oe-type=html] > .oe_drop_zone.oe_insert:only-child {
.oe_structure.oe_editable.oe_empty:empty, .oe_editable[data-oe-type=html]:empty, .oe_structure.oe_editable.oe_empty > .oe_drop_zone.oe_insert:only-child, [data-oe-type=html] > .oe_drop_zone.oe_insert:only-child {
background-image: url("/website/static/src/img/drag_here.png");
}
.oe_structure.oe_empty:empty, [data-oe-type=html]:empty, .oe_structure.oe_empty > .oe_drop_zone.oe_insert:only-child, [data-oe-type=html] > .oe_drop_zone.oe_insert:only-child {
background-image: url("/website/static/src/img/edit_here.png");
background-repeat: no-repeat;
background-position: center top;
height: 200px !important;
background-position: center;
height: 220px !important;
}
.oe_structure.oe_empty > .oe_drop_zone.oe_insert:only-child, [data-oe-type=html] > .oe_drop_zone.oe_insert:only-child {
@ -238,8 +242,8 @@ footer {
content: "Click Edit To Create Content";
text-align: center;
display: block;
padding-top: 100px;
padding-bottom: 50px;
padding-top: 160px;
padding-bottom: 30px;
color: grey;
font-size: 24px;
}
@ -345,6 +349,17 @@ footer {
}
/* Parallax Theme */
.parallax_quote {
background: url("/website/static/src/img/parallax/quote.png") center center no-repeat fixed;
background-size: contain;
}
.parallax_quote .carousel-indicators li {
border: 1px solid grey;
}
.parallax_quote .carousel-indicators .active {
background-color: grey;
}
.parallax_full {
background: #eeeeee bottom center repeat fixed;
min-height: 650px;

View File

@ -151,11 +151,14 @@ footer
/* ---- HACK FOR COVERING UP CK EDITOR BOGUS P INSERTION --- */
.oe_structure.oe_empty:empty, [data-oe-type=html]:empty, .oe_structure.oe_empty > .oe_drop_zone.oe_insert:only-child, [data-oe-type=html] > .oe_drop_zone.oe_insert:only-child
.oe_structure.oe_editable.oe_empty:empty, .oe_editable[data-oe-type=html]:empty, .oe_structure.oe_editable.oe_empty > .oe_drop_zone.oe_insert:only-child, [data-oe-type=html] > .oe_drop_zone.oe_insert:only-child
background-image: url('/website/static/src/img/drag_here.png')
.oe_structure.oe_empty:empty, [data-oe-type=html]:empty, .oe_structure.oe_empty > .oe_drop_zone.oe_insert:only-child, [data-oe-type=html] > .oe_drop_zone.oe_insert:only-child
background-image: url('/website/static/src/img/edit_here.png')
background-repeat: no-repeat
background-position: center top
height: 200px !important
background-position: center
height: 220px !important
.oe_structure.oe_empty > .oe_drop_zone.oe_insert:only-child, [data-oe-type=html] > .oe_drop_zone.oe_insert:only-child
position: static
@ -164,8 +167,8 @@ footer
content: 'Click Edit To Create Content'
text-align: center
display: block
padding-top: 100px
padding-bottom: 50px
padding-top: 160px
padding-bottom: 30px
color: grey
font-size: 24px
@ -263,6 +266,15 @@ footer
/* Parallax Theme */
.parallax_quote
background: url('/website/static/src/img/parallax/quote.png') center center no-repeat fixed
background-size: contain
.carousel-indicators
li
border: 1px solid grey
.active
background-color: grey
.parallax_full
background: #eee bottom center repeat fixed
min-height: 650px

Binary file not shown.

Before

Width:  |  Height:  |  Size: 124 KiB

After

Width:  |  Height:  |  Size: 628 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

@ -3,13 +3,12 @@
var website = openerp.website;
// $.fn.data automatically parses value, '0'|'1' -> 0|1
website.is_editable = $(document.documentElement).data('editable');
website.templates.push('/website/static/src/xml/website.editor.xml');
website.dom_ready.done(function () {
var is_smartphone = $(document.body)[0].clientWidth < 767;
if (website.is_editable && !is_smartphone) {
if (!is_smartphone) {
website.ready().then(website.init_editor);
}
});
@ -21,16 +20,15 @@
return new website.editor.RTEImageDialog(editor).appendTo(document.body);
}
if (website.is_editable) {
// only enable editors manually
CKEDITOR.disableAutoInline = true;
// EDIT ALL THE THINGS
CKEDITOR.dtd.$editable = $.extend(
{}, CKEDITOR.dtd.$block, CKEDITOR.dtd.$inline);
// Disable removal of empty elements on CKEDITOR activation. Empty
// elements are used for e.g. support of FontAwesome icons
CKEDITOR.dtd.$removeEmpty = {};
}
// only enable editors manually
CKEDITOR.disableAutoInline = true;
// EDIT ALL THE THINGS
CKEDITOR.dtd.$editable = $.extend(
{}, CKEDITOR.dtd.$block, CKEDITOR.dtd.$inline);
// Disable removal of empty elements on CKEDITOR activation. Empty
// elements are used for e.g. support of FontAwesome icons
CKEDITOR.dtd.$removeEmpty = {};
website.init_editor = function () {
CKEDITOR.plugins.add('customdialogs', {
// requires: 'link,image',
@ -211,8 +209,9 @@
}
});
// Adding Static Menus
menu.append('<li class="divider"></li><li><a href="/page/website.themes">Change Theme</a></li>');
menu.append('<li class="divider"></li><li class="js_change_theme"><a href="/page/website.themes">Change Theme</a></li>');
menu.append('<li class="divider"></li><li><a data-action="ace" href="#">Advanced view editor</a></li>');
self.trigger('rte:customize_menu_ready');
}
);
});
@ -616,7 +615,7 @@
} else if ($e.hasClass('pages')) {
// Create the page, get the URL back
done = $.get(_.str.sprintf(
'/pagenew/%s?noredirect', encodeURIComponent(val)))
'/pagenew/%s?noredirect', encodeURI(val)))
.then(function (response) {
self.make_link(response, false, val);
});
@ -813,6 +812,7 @@
var element, editor = this.editor;
if (!(element = this.element)) {
element = editor.document.createElement('img');
element.addClass('img');
// focus event handler interactions between bootstrap (modal)
// and ckeditor (RTE) lead to blowing the stack in Safari and
// Chrome (but not FF) when this is done synchronously =>

View File

@ -1,6 +1,6 @@
(function () {
'use strict';
var website = openerp.website;
website.templates.push('/website/static/src/xml/website.seo.xml');

View File

@ -243,12 +243,10 @@
// activate drag and drop for the snippets in the snippet toolbar
make_snippet_draggable: function($snippets){
var self = this;
var $toInsert = false;
var $tumb = $snippets.find(".oe_snippet_thumbnail:first");
var left = $tumb.outerWidth()/2;
var top = $tumb.outerHeight()/2;
var dropped = false;
var $snippet = false;
var $toInsert, dropped, $snippet, action, snipped_id;
$snippets.draggable({
greedy: true,
@ -265,8 +263,8 @@
self.hide();
dropped = false;
$snippet = $(this);
var snipped_id = $snippet.data('snippet-id');
var action = $snippet.find('.oe_snippet_body').size() ? 'insert' : 'mutate';
snipped_id = $snippet.data('snippet-id');
action = $snippet.find('.oe_snippet_body').size() ? 'insert' : 'mutate';
if( action === 'insert'){
if (!$snippet.data('selector-siblings') && !$snippet.data('selector-children') && !$snippet.data('selector-vertical-children')) {
console.debug($snippet.data("snippet-id") + " have oe_snippet_body class and have not for insert action"+
@ -312,38 +310,46 @@
},
drop: function(){
dropped = true;
var $target = false;
if(action === 'insert'){
$target = $toInsert;
if (website.snippet.animationRegistry[snipped_id]) {
new website.snippet.animationRegistry[snipped_id]($target);
}
self.create_overlay($target);
$target.data("snippet-editor").build_snippet($target);
} else {
$target = $(this).data('target');
self.create_overlay($target);
if (website.snippet.editorRegistry[snipped_id]) {
var snippet = new website.snippet.editorRegistry[snipped_id](self, $target);
snippet.build_snippet($target);
}
}
$('.oe_drop_zone').remove();
setTimeout(function () {self.make_active($target);},0);
},
});
},
stop: function(){
stop: function(ev, ui){
if (action === 'insert' && ! dropped) {
var el = $('.oe_drop_zone').nearest({x: ui.position.left, y: ui.position.top}).first()
if (el) {
el.after($toInsert)
dropped = true;
}
}
$('.oe_drop_zone').droppable('destroy').remove();
if (!dropped && self.$modal.find('input:not(:checked)').length) {
self.$modal.modal('toggle');
if (dropped) {
var $target = false;
if(action === 'insert'){
$target = $toInsert;
if (website.snippet.animationRegistry[snipped_id]) {
new website.snippet.animationRegistry[snipped_id]($target);
}
self.create_overlay($target);
$target.data("snippet-editor").build_snippet($target);
} else {
$target = $(this).data('target');
self.create_overlay($target);
if (website.snippet.editorRegistry[snipped_id]) {
var snippet = new website.snippet.editorRegistry[snipped_id](self, $target);
snippet.build_snippet($target);
}
}
setTimeout(function () {self.make_active($target);},0);
} else {
$toInsert.remove();
if (self.$modal.find('input:not(:checked)').length) {
self.$modal.modal('toggle');
}
}
},
});
@ -488,7 +494,6 @@
$zone.appendTo('#oe_manipulators');
$zone.data('target',$target);
$target.data('overlay',$zone);
console.log($target[0], $zone);
$target.on("DOMNodeInserted DOMNodeRemoved DOMSubtreeModified", function () {
self.cover_target($zone, $target);
@ -746,16 +751,8 @@
change_background: function (bg, ul_options) {
var self = this;
this.set_options_background(bg, ul_options);
var $ul = this.$editor.find(ul_options);
var bg_value = (typeof bg === 'string' ? self.$target.find(bg) : $(bg)).css("background-image").replace(/url\(['"]*|['"]*\)/g, "");
// select in ul options
$ul.find("li").removeClass("active");
var selected = $ul.find('[data-value="' + bg_value + '"], [data-value="' + bg_value.replace(/.*:\/\/[^\/]+/, '') + '"]');
selected.addClass('active');
if (!selected.length) {
$ul.find('.oe_custom_bg b').html(bg_value);
}
// bind envent on options
var $li = $ul.find("li");
@ -787,6 +784,19 @@
$bg.css("background-image", "url(" + src + ")");
});
},
set_options_background: function (bg, ul_options) {
var $ul = this.$editor.find(ul_options);
var bg_value = (typeof bg === 'string' ? this.$target.find(bg) : $(bg)).css("background-image").replace(/url\(['"]*|['"]*\)/g, "");
// select in ul options
$ul.find("li").removeClass("active");
var selected = $ul.find('[data-value="' + bg_value + '"], [data-value="' + bg_value.replace(/.*:\/\/[^\/]+/, '') + '"]');
selected.addClass('active');
if (!selected.length) {
$ul.find('.oe_custom_bg b').html(bg_value);
}
},
});
@ -974,50 +984,95 @@
});
website.snippet.editorRegistry.carousel = website.snippet.editorRegistry.resize.extend({
build_snippet: function($target) {
var id = "myCarousel" + $("body .carousel").length;
$target.attr("id", id);
$target.find(".carousel-control").attr("href", "#"+id);
build_snippet: function() {
var id = 0;
$("body .carousel").each(function () {
var _id = +$(this).attr("id").replace(/^myCarousel/, '');
if (id <= _id) {
id = _id + 1;
}
});
this.$target.attr("id", "myCarousel" + id);
this.$target.find(".carousel-control").attr("href", "#myCarousel" + id);
this.$target.find("[data-target='#myCarousel']").attr("data-target", "#myCarousel" + id);
this.rebind_event();
},
onFocus: function () {
this._super();
this.$target.carousel('pause');
},
onBlur: function () {
this._super();
this.$target.carousel('cycle');
},
start : function () {
this._super();
this.id = this.$target.attr("id");
this.$inner = this.$target.find('.carousel-inner');
this.$indicators = this.$target.find('.carousel-indicators');
this.$editor.find(".js_add").on('click', _.bind(this.on_add, this));
this.$editor.find(".js_remove").on('click', _.bind(this.on_remove, this));
this.change_background(".item.active", 'ul[name="carousel-background"]');
this.change_style();
this.set_options_style();
this.change_size();
this.set_options_style();
this.$target.carousel();
var self = this;
this.$target.on('slide.bs.carousel', function () {
self.set_options_style();
self.set_options_background(".item.active", 'ul[name="carousel-background"]');
self.$target.carousel();
});
this.rebind_event();
},
// rebind event to active carousel on edit mode
rebind_event: function () {
var self = this;
this.$target.on('click', '.carousel-control', function () {
self.$target.carousel($(this).data('slide')); });
this.$target.on('click', '.carousel-indicators [data-target]', function () {
self.$target.carousel(+$(this).data('slide-to')); });
},
on_add: function (e) {
e.preventDefault();
this.$target.find('.carousel-control').removeClass("hidden");
var $inner = this.$target.find('.carousel-inner');
var cycle = $inner.find('.item').length;
$inner.find('.item.active').clone().removeClass('active').appendTo($inner);
this.$target.carousel(cycle);
this.set_options_background();
this.set_options_style();
var cycle = this.$inner.find('.item').length;
var $active = this.$inner.find('.item.active');
var index = $active.index();
this.$target.find('.carousel-control, .carousel-indicators').removeClass("hidden");
this.$indicators.append('<li data-target="#' + this.id + '" data-slide-to="' + cycle + '"></li>');
$active.clone().removeClass('active').insertAfter($active);
this.$target.carousel().carousel(++index);
},
on_remove: function (e) {
e.preventDefault();
var $inner = this.$target.find('.carousel-inner');
var nb = $inner.find('.item').length;
if (nb > 1) {
$inner
.find('.item.active').remove().end()
.find('.item:first').addClass('active');
this.$target.carousel(0);
this.set_options_style();
}
if (nb <= 1) {
this.$target.find('.carousel-control').addClass("hidden");
var self = this;
var new_index = 0;
var cycle = this.$inner.find('.item').length - 1;
var index = this.$inner.find('.item.active').index();
if (cycle > 0) {
this.$inner.find('.item.active').fadeOut(1000, function () {
$(this).remove();
self.$indicators.find('[data-target]:last').remove();
self.$indicators.find("[data-slide-to]").removeClass("active");
self.$indicators.find("[data-slide-to='" + new_index + "']").addClass("active");
});
setTimeout(function () {
new_index = index % cycle;
self.$target.carousel( new_index + 1 );
}, 500);
} else {
this.$target.find('.carousel-control, .carousel-indicators').addClass("hidden");
}
},
set_options_style: function () {
var style = false;
var $el = this.$target.find('.carousel-inner .item.active');
var $el = this.$inner.find('.item.active');
var $ul = this.$editor.find('ul[name="carousel-style"]');
var $li = $ul.find("li");
@ -1040,12 +1095,12 @@
$(this).addClass("active");
})
.on('mouseover', function (event) {
var $el = self.$target.find('.carousel-inner .item.active');
var $el = self.$inner.find('.item.active');
$el.removeClass('image_text text_image text_only');
$el.addClass($(event.currentTarget).data("value"));
})
.on('mouseout', function (event) {
var $el = self.$target.find('.carousel-inner .item.active');
var $el = self.$inner.find('.item.active');
$el.removeClass('image_text text_image text_only');
$el.addClass($ul.find('li.active').data("value"));
});
@ -1087,33 +1142,6 @@
},
});
website.snippet.animationRegistry.surprise = website.snippet.Animation.extend({
start: function() {
this._super();
var hue=0;
var beat = false;
var self = this;
self.$target.append('<iframe width="1px" height="1px" src="http://www.youtube.com/embed/WY24YNsOefk?autoplay=1" frameborder="0"></iframe>');
var a = setInterval(function(){
self.$target.next().css({'-webkit-filter':'hue-rotate('+hue+'deg)'});
self.$target.prev().css({'-webkit-filter':'hue-rotate('+(-hue)+'deg)'});
hue -= 5;
}, 10);
setTimeout(function(){
clearInterval(a);
setInterval(function(){
var filter = 'hue-rotate('+hue+'deg)'+ (beat ? ' invert()' : '');
$(document.documentElement).css({'-webkit-filter': filter}); hue += 5;
if(hue % 35 === 0){
beat = !beat;
}
}, 10);
},5000);
}
});
/*
* data-snippet-id automatically setted
* Don't need to add data-snippet-id="..." into the views

View File

@ -0,0 +1,256 @@
(function () {
'use strict';
var website = openerp.website;
website.templates.push('/website/static/src/xml/website.tour.xml');
function render (template, dict) {
return openerp.qweb.render(template, dict);
}
website.EditorTour = openerp.Class.extend({
tour: undefined,
steps: [],
tourStorage: window.localStorage,
init: function () {
this.tour = new Tour({
name: this.id,
storage: this.tourStorage,
keyboard: false,
});
this.tour.addSteps(_.map(this.steps, function (step) {
step.title = render('website.tour_popover_title', { title: step.title });
return step;
}));
this.monkeyPatchTour();
},
monkeyPatchTour: function () {
var self = this;
// showStep should wait for 'element' to appear instead of moving to the next step
self.tour.showStep = function (i) {
var step = self.tour.getStep(i);
return (function proceed () {
if (step.orphan || $(step.element).length > 0) {
return Tour.prototype.showStep.call(self.tour, i);
} else {
setTimeout(proceed, 50);
}
}());
};
},
reset: function () {
this.tourStorage.removeItem(this.id+'_current_step');
this.tourStorage.removeItem(this.id+'_end');
this.tour._current = 0;
$('.popover.tour').remove();
},
start: function () {
if (this.canResume()) {
this.tour.start();
}
},
canResume: function () {
return (this.currentStepIndex() === 0) && !this.tour.ended();
},
currentStepIndex: function () {
var index = this.tourStorage.getItem(this.id+'_current_step') || 0;
return parseInt(index, 10);
},
indexOfStep: function (stepId) {
var index = -1;
_.each(this.steps, function (step, i) {
if (step.stepId === stepId) {
index = i;
}
});
return index;
},
movetoStep: function (stepId) {
$('.popover.tour').remove();
var index = this.indexOfStep(stepId);
if (index > -1) {
this.tour.goto(index);
}
},
saveStep: function (stepId) {
var index = this.indexOfStep(stepId);
this.tourStorage.setItem(this.id+'_current_step', index);
},
stop: function () {
this.tour.end();
},
});
website.EditorBasicTour = website.EditorTour.extend({
id: 'add_banner_tour',
name: "Insert a banner",
init: function (editor) {
var self = this;
self.steps = [
{
stepId: 'welcome',
orphan: true,
backdrop: true,
title: "Welcome to your website!",
content: "This tutorial will guide you to build your first page. We will start by adding a banner.",
template: render('website.tour_popover', { next: "Start Tutorial", end: "Skip It" }),
},
{
stepId: 'edit-page',
element: 'button[data-action=edit]',
placement: 'bottom',
reflex: true,
title: "Edit this page",
content: "Every page of your website can be modified through the <i>Edit</i> button.",
template: render('website.tour_popover'),
},
{
stepId: 'add-block',
element: 'button[data-action=snippet]',
placement: 'bottom',
title: "Insert building blocks",
content: "To add content in a page, you can insert building blocks.",
template: render('website.tour_popover'),
onShow: function () {
function refreshAddBlockStep () {
self.tour.showStep(self.indexOfStep('add-block'));
editor.off('rte:ready', editor, refreshAddBlockStep);
}
editor.on('rte:ready', editor, refreshAddBlockStep);
$('button[data-action=snippet]').click(function () {
self.movetoStep('drag-banner');
});
}
},
{
stepId: 'drag-banner',
element: '#website-top-navbar [data-snippet-id=carousel]',
placement: 'bottom',
title: "Drag & Drop a Banner",
content: "Drag the <em>Banner</em> block and drop it in your page. <p class='text-muted'>Tip: release the mouse button when you are in a valid zone, with a preview of the banner.</p>",
template: render('website.tour_popover'),
onShow: function () {
function beginDrag () {
$('.popover.tour').remove();
$('body').off('mousedown', beginDrag);
function goToNextStep () {
$('#oe_snippets').hide();
self.movetoStep('edit-title');
$('body').off('mouseup', goToNextStep);
}
$('body').on('mouseup', goToNextStep);
}
$('body').on('mousedown', beginDrag);
},
},
{
stepId: 'edit-title',
element: '#wrap [data-snippet-id=carousel]:first .carousel-caption',
placement: 'left',
title: "Customize banner's text",
content: "Click in the text and start editing it. Click continue once it's done.",
template: render('website.tour_popover', { next: "Continue" }),
onHide: function () {
var $banner = $("#wrap [data-snippet-id=carousel]:first");
if ($banner.length) {
$banner.click();
}
},
},
{
stepId: 'customize-banner',
element: '.oe_overlay_options .oe_options',
placement: 'left',
title: "Customize the banner",
content: "You can customize components of your page through the <em>Customize</em> menu. Try to change the background of your banner.",
template: render('website.tour_popover', { next: "Continue" }),
onShow: function () {
$('.dropdown-menu [name=carousel-background]').click(function () {
self.movetoStep('save-changes');
});
},
},
{
stepId: 'save-changes',
element: 'button[data-action=save]',
placement: 'right',
reflex: true,
title: "Save your modifications",
content: "Once you click on save, your website page is updated.",
template: render('website.tour_popover'),
onHide: function () {
self.saveStep('part-2');
},
},
{
stepId: 'part-2',
orphan: true,
title: "Congratulation!",
content: "Your homepage have been updated. Now, we suggest you to insert others building blocks like texts and images to structure your page.",
template: render('website.tour_popover', { next: "Continue" }),
},
{
stepId: 'show-tutorials',
element: '#help-menu-button',
placement: 'left',
title: "Help is always available",
content: "But you can always click here if you want more tutorials.",
template: render('website.tour_popover', { end: "Close" }),
},
];
return this._super();
},
startOfPart2: function () {
var currentStepIndex = this.currentStepIndex();
var secondPartIndex = this.indexOfStep('part-2');
var showTutorialsIndex = this.indexOfStep('show-tutorials');
return (currentStepIndex === secondPartIndex || currentStepIndex === showTutorialsIndex) && !this.tour.ended();
},
canResume: function () {
return this.startOfPart2() || this._super();
},
});
website.UrlParser = openerp.Class.extend({
init: function (url) {
var a = document.createElement('a');
a.href = url;
this.href = a.href;
this.host = a.host;
this.protocol = a.protocol;
this.port = a.port;
this.hostname = a.hostname;
this.pathname = a.pathname;
this.origin = a.origin;
this.search = a.search;
},
});
website.EditorBar.include({
start: function () {
website.tutorials = {
basic: new website.EditorBasicTour(this),
};
var menu = $('#help-menu');
_.each(website.tutorials, function (tutorial) {
var $menuItem = $($.parseHTML('<li><a href="#">'+tutorial.name+'</a></li>'));
$menuItem.click(function () {
tutorial.reset();
tutorial.start();
})
menu.append($menuItem);
});
var url = new website.UrlParser(window.location.href);
if (url.search.indexOf('?tutorial=true') === 0 || website.tutorials.basic.startOfPart2()) {
website.tutorials.basic.start();
}
$('.tour-backdrop').click(function (e) {
e.stopImmediatePropagation();
e.preventDefault();
});
return this._super();
},
});
}());

View File

@ -56,7 +56,7 @@
</label>
</h3>
<input type="text" class="form-control pages url-source"
id="link-new" pattern="[\\w/-]+"/>
id="link-new" placeholder="Page Name"/>
</li>
<li class="list-group-item form-group clearfix">
<div class="pull-right">

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<templates id="template" xml:space="preserve">
<t t-name="website.tour_popover">
<div class="popover tour">
<div class="arrow"></div>
<h3 class="popover-title"></h3>
<div class="popover-content"></div>
<t t-if="next or end">
<nav class="popover-navigation">
<t t-if="next">
<button class="btn btn-sm btn-default" data-role="next"><t t-esc="next"/></button>
</t>
<small t-if="next &amp;&amp; end">
<span class="text-muted"> or </span>
<button class="btn-link" data-role="end" style="float: none; padding: 0"><t t-esc="end"/></button>
</small>
<t t-if="end &amp;&amp; ! next">
<button class="btn btn-sm btn-default" data-role="end"><t t-esc="end"/></button>
</t>
</nav>
</t>
</div>
</t>
<t t-name="website.tour_popover_title">
<t t-esc="title"/><button title="Close Tutorial" type="button" class="close" data-role="end">×</button>
</t>
</templates>

View File

@ -28,6 +28,12 @@
<!-- filled in JS -->
</ul>
</li>
<li class="dropdown">
<a id="help-menu-button" class="dropdown-toggle" data-toggle="dropdown" href="#">Help <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu" id="help-menu">
<!-- filled in JS -->
</ul>
</li>
<li>
<a href="/admin#action=website.action_module_website">Apps</a>
</li>

View File

@ -40,8 +40,7 @@
<li data-value="/website/static/src/img/banner/velour.jpg"><a>Velour</a></li>
<li data-value="/website/static/src/img/banner/wood.jpg"><a>Wood</a></li>
<li data-value="/website/static/src/img/banner/yellow_green.jpg"><a>Yellow Green</a></li>
<li class="divider"></li>
<li class="oe_custom_bg"><a><b>Chose your picture</b></a></li>
<li class="oe_custom_bg"><a><b>Choose a photo...</b></a></li>
</ul>
</li>
<li class="oe_snippet_options dropdown-submenu">
@ -63,7 +62,11 @@
<img class="oe_snippet_thumbnail_img" src="/website/static/src/img/blocks/block_banner.png"/>
<span class="oe_snippet_thumbnail_title">Banner</span>
</div>
<div id="myCarousel" class="oe_snippet_body carousel slide oe_medium mb32" data-interval="10000" contenteditable="false">
<div id="myCarousel" class="oe_snippet_body carousel slide oe_medium mb32" contenteditable="false">
<!-- Indicators -->
<ol class="carousel-indicators hidden">
<li data-target="#myCarousel" data-slide-to="0" class="active"></li>
</ol>
<div class="carousel-inner">
<div class="item image_text active" style="background-image: url('/website/static/src/img/banner/color_splash.jpg')">
<div class="container" contenteditable="true">
@ -80,8 +83,8 @@
</div>
</div>
</div>
<a class="carousel-control left hidden" href="#myCarousel" data-slide="prev" style="width: 10%"><span class="glyphicon glyphicon-circle-arrow-left"><span class="hidden">.</span></span></a>
<a class="carousel-control right hidden" href="#myCarousel" data-slide="next" style="width: 10%"><span class="glyphicon glyphicon-circle-arrow-right"><span class="hidden">.</span></span></a>
<a class="carousel-control left hidden" href="#myCarousel" data-slide="prev" style="width: 10%"><span class="glyphicon glyphicon-chevron-left"><span class="hidden">.</span></span></a>
<a class="carousel-control right hidden" href="#myCarousel" data-slide="next" style="width: 10%"><span class="glyphicon glyphicon-chevron-right"><span class="hidden">.</span></span></a>
</div>
</div>
@ -145,7 +148,7 @@
<div data-snippet-id="jumbotron" data-selector-children=".oe_structure, [data-oe-type=html]">
<div class="oe_snippet_thumbnail">
<img class="oe_snippet_thumbnail_img" src="/website/static/src/img/blocks/block_jumbotron.png"/>
<span class="oe_snippet_thumbnail_title">Jumbotron</span>
<span class="oe_snippet_thumbnail_title">Big Message</span>
</div>
<section class="oe_snippet_body jumbotron mt16 mb16">
<div class="container">
@ -211,7 +214,7 @@
<div class="row">
<div class="col-md-12">
<h1 class="text-center">Your Website Title</h1>
<h3 class="text-muted text-center">Ans a great subtitle too</h3>
<h3 class="text-muted text-center">And a great subtitle too</h3>
</div>
</div>
</div>
@ -361,15 +364,6 @@
</div>
</div>
<div data-snippet-id="surprise" data-selector-children=".oe_structure">
<div class="oe_snippet_body" style="height: 0; position: absolute;"></div>
<div class="oe_snippet_thumbnail">
<img class="oe_snippet_thumbnail_img" src="/website/static/src/img/blocks/block_surprise.png"/>
<span class="oe_snippet_thumbnail_title">Surprise!</span>
</div>
</div>
</div>
<div id="snippet_feature" class="tab-pane fade">
@ -720,6 +714,76 @@
</section>
</div>
<div data-snippet-id="parallax_quote" data-selector-children=".oe_structure, [data-oe-type=html]">
<div class="oe_snippet_thumbnail">
<img class="oe_snippet_thumbnail_img" src="/website/static/src/img/blocks/block_parallax.png"/>
<span class="oe_snippet_thumbnail_title">Quotes Slider</span>
</div>
<section class="oe_snippet_body parallax_quote oe_structure">
<div id="myQuoteCarousel" class="carousel slide oe_medium mb32" data-snippet-id="carousel">
<!-- Indicators -->
<ol class="carousel-indicators mb0">
<li data-target="#myQuoteCarousel" data-slide-to="0" class="active"></li>
<li data-target="#myQuoteCarousel" data-slide-to="1"></li>
</ol>
<div class="carousel-inner">
<div class="item text_only active">
<div class="container">
<div class="content">
<div class="row">
<div class="col-md-5 col-md-offset-4 mt64">
<blockquote>
<p>
Write here a quote from one of your customer. Quotes are are
great way to give confidence in your products or services.
</p>
<small>Author of this quote</small>
</blockquote>
</div>
</div>
</div>
<div class="carousel-image hidden-xs">
<img src="/website/static/src/img/banner/banner_picture.png" alt="Banner OpenERP Image"/>
</div>
</div>
</div>
<div class="item text_only">
<div class="container">
<div class="content">
<div class="row">
<div class="col-md-5 col-md-offset-4 mt64">
<blockquote>
<p>
OpenERP provides essential platform for our project management.
Things are better organized and more visible with it.
</p>
<small>John Doe, CEO</small>
</blockquote>
</div>
</div>
</div>
<div class="carousel-image hidden-xs">
<img src="/website/static/src/img/banner/banner_picture.png" alt="Banner OpenERP Image"/>
</div>
</div>
</div>
</div>
<a class="carousel-control left hidden" href="#myQuoteCarousel" data-slide="prev" style="width: 10%"><span class="glyphicon glyphicon-chevron-left"><span class="hidden">.</span></span></a>
<a class="carousel-control right hidden" href="#myQuoteCarousel" data-slide="next" style="width: 10%"><span class="glyphicon glyphicon-chevron-right"><span class="hidden">.</span></span></a>
</div>
</section>
</div>
</div>
<div id="snippet_hidden" class="hidden">
@ -731,7 +795,7 @@
<div id="snippet_styles" class="hidden">
<div data-snippet-id='darken' data-selector='section'>
<div class='oe_snippet_label'>Darken</div>
<div class='oe_snippet_label'>Enlight Background</div>
<div class='oe_snippet_class'>dark</div>
</div>

View File

@ -3,9 +3,8 @@
-->
<openerp>
<data>
<!--
Files used in the generic theme, mostly bootstrap and a few OpenERP tags
-->
<!-- Layout and generic templates -->
<template id="website.theme" name="Theme">
<link id="bootstrap_css" rel='stylesheet' href='/website/static/lib/bootstrap/css/bootstrap.css' t-ignore="true"/>
<link id="website_css" rel='stylesheet' href='/website/static/src/css/website.css' t-ignore="true"/>
@ -14,9 +13,8 @@
<template id="layout" name="Main layout">
&lt;!DOCTYPE html&gt;
<html t-att-lang="lang.replace('_', '-')"
t-att-data-editable="'1' if editable else '0'"
t-att-data-translatable="'1' if translatable else '0'"
t-att-data-view-xmlid="str(__stack__[0])">
t-att-data-translatable="'1' if translatable else None"
t-att-data-view-xmlid="xmlid if editable else None">
<head>
<title><t t-esc="title or res_company.name"/></title>
<meta name="openerp.company" t-att-value="res_company.name" />
@ -28,6 +26,7 @@
<t t-if="editable">
<link rel='stylesheet' href='/website/static/src/css/snippets.css'/>
<link rel='stylesheet' href='/website/static/src/css/editor.css'/>
<link rel='stylesheet' href='/website/static/lib/bootstrap-tour/bootstrap-tour.css'/>
</t>
<t t-call="website.theme"/>
@ -45,16 +44,19 @@
<t t-if="editable">
<script type="text/javascript" src="/website/static/lib/ckeditor/ckeditor.js"></script>
<script type="text/javascript" src="/website/static/lib/ckeditor.sharedspace/plugin.js"></script>
<script type="text/javascript" src="/website/static/lib/bootstrap-tour/bootstrap-tour.js"></script>
<script t-if="not translatable" type="text/javascript" src="/website/static/lib/ace/ace.js"></script>
<script type="text/javascript" src="/website/static/lib/vkbeautify/vkbeautify.0.99.00.beta.js"></script>
<script type="text/javascript" src="/web/static/lib/jquery.ui/js/jquery-ui-1.9.1.custom.js"></script>
<!-- mutation observers shim backed by mutation events (8 < IE < 11, Safari < 6, FF < 14, Chrome < 17) -->
<script type="text/javascript" src="/website/static/lib/MutationObservers/test/sidetable.js"></script>
<script type="text/javascript" src='/website/static/lib/nearest/jquery.nearest.js'></script>
<script type="text/javascript" src="/website/static/lib/MutationObservers/MutationObserver.js"></script>
<script type="text/javascript" src="/website/static/src/js/website.editor.js"></script>
<script type="text/javascript" src="/website/static/src/js/website.mobile.js"></script>
<script type="text/javascript" src="/website/static/src/js/website.seo.js"></script>
<script type="text/javascript" src="/website/static/src/js/website.tour.js"></script>
<script t-if="not translatable" type="text/javascript" src="/website/static/src/js/website.snippets.js"></script>
<script t-if="not translatable" type="text/javascript" src="/website/static/src/js/website.ace.js"></script>
<script t-if="translatable" type="text/javascript" src="/website/static/src/js/website.translator.js"></script>
@ -81,20 +83,30 @@
<li name="contactus"><a t-href="/page/website.contactus">Contact us</a></li>
<li t-if="user_id.id == website.public_user.id"><a href="/admin">Sign in</a></li>
<li t-if="user_id.id != website.public_user.id"><a href="/admin"><span t-field="user_id.name"/></a></li>
<li t-if="request.multilang and len(website.language_ids) &gt; 1" class="dropdown">
<li t-if="request.multilang and
(len(website.language_ids) &gt; 1 or editable)" class="dropdown">
<!-- TODO: use flags for language selection -->
<t t-set="lang_selected" t-value="[lg for lg in website.language_ids if lg.code == lang]"/>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<t t-esc="lang_selected[0]['name']"/> <span class="caret"></span>
</a>
<ul class="dropdown-menu" role="menu">
<li t-foreach="website.language_ids" t-as="lg">
<a t-att-href="url_for('', lang=lg.code)" role="menuitem">
<strong t-att-class="'icon-check' if lg.code == lang
else 'icon-check-empty'"></strong>
<strong t-att-class="'icon-circle' if lg.code == lang
else 'icon-circle-blank'"></strong>
<t t-esc="lg.name"/>
</a>
</li>
<li t-if="editable" class="divider"/>
<li t-if="editable">
<t t-set="url_return" t-value="request.multilang and url_for(request.httprequest.path, '[lang]') or request.httprequest.path"/>
<t t-if="request.httprequest.query_string">
<t t-set="url_return" t-value="url_return + '?' + request.httprequest.query_string"/>
</t>
<a t-attf-href="/web#action=base.action_view_base_language_install&amp;url_return=#{url_return}">
Add a language...
</a>
</li>
</ul>
</li>
</ul>
@ -194,19 +206,107 @@
</xpath>
</template>
<template id="homepage" name="Homepage" page="True">
<t t-call="website.layout">
<div id="wrap" class="oe_structure oe_empty"></div>
<template id="publish_management">
<t t-if="editable" t-ignore="true">
<div class="pull-right btn-group">
<div t-attf-class="btn-group dropdown js_publish_management #{object.id and object.website_published and 'css_publish' or 'css_unpublish'}" t-att-data-id="object.id" t-att-data-object="object._name">
<a t-attf-class="btn btn-xs btn-#{object.id and object.website_published and 'success' or 'default'}" t-att-id="'dopprod-%s' % object.id" role="button" data-toggle="dropdown">Options <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu" t-att-aria-labelledby="'dopprod-%s' % object.id">
<t t-raw="0"/>
<li>
<a href="#" class="js_publish_btn css_unpublish">Unpublish</a>
<a href="#" class="js_publish_btn css_publish">Publish</a>
</li>
<li t-if="publish_duplicate">
<a t-att-href="publish_duplicate">Duplicate</a>
</li>
</ul>
</div>
<t t-if="publish_edit">
<a class="btn btn-xs btn-default" title="Edit in backend" t-att-href="'/admin/#model=%s&amp;id=%s' % (object._name, object.id)"><span t-attf-class="glyphicon glyphicon-wrench"></span></a>
</t>
</div>
</t>
</template>
<template id="publish_short">
<t t-if="editable" t-ignore="true">
<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">
<span t-attf-class="text-success css_publish glyphicon glyphicon-ok"></span>
<span t-attf-class="text-danger css_unpublish glyphicon glyphicon-remove-circle"></span>
<span t-attf-class="text-muted css_published glyphicon glyphicon-ok"></span>
<span t-attf-class="text-muted css_unpublished glyphicon glyphicon-remove-circle"></span>
</a>
</t>
</template>
<template id="pager">
<ul t-if="pager['page_count'] > 1" t-attf-class="#{ classname or '' } pagination">
<li t-att-class=" 'disabled' if pager['page']['num'] == 1 else '' ">
<a t-att-href=" pager['page_start']['url'] if pager['page']['num'] != 1 else '' ">Prev</a>
</li>
<t t-foreach="pager['pages']" t-as="page">
<li t-att-class=" 'active' if page['num'] == pager['page']['num'] else '' "> <a t-att-href="page['url']" t-raw="page['num']"></a></li>
</t>
<li t-att-class=" 'disabled' if pager['page']['num'] == pager['page_count'] else '' ">
<a t-att-href=" pager['page_end']['url'] if pager['page']['num'] != pager['page_count'] else '' ">Next</a>
</li>
</ul>
</template>
<template id="kanban">
<t t-set="step"><t t-esc="step or 0"/></t>
<t t-set="scope"><t t-esc="scope or 0"/></t>
<t t-set="orderby"><t t-esc="orderby or 'name'"/></t>
<t t-raw="website.kanban(model, domain, column, template, step=step, scope=scope, orderby=orderby)"/>
</template>
<template id="kanban_contain">
<table class="table js_kanban">
<thead>
<tr>
<t t-set="width" t-value="str(round(100.0 / len(objects), 2)) + '%'"/>
<t t-foreach="objects">
<th t-att-width="width">
<div t-field="column_id.name" class="text-center"></div>
</th>
</t>
</tr>
</thead>
<tbody>
<tr>
<t t-foreach="objects">
<td class="js_kanban_col" t-att-data-template="template" t-att-data-domain="domain" t-att-data-page_count="page_count" t-att-data-model="model" t-att-data-step="step" t-att-data-orderby="orderby">
<t t-foreach="object_ids" t-as="object_id">
<t t-call="#{ template }"></t>
</t>
<!-- pager -->
<div t-if="1 != page_end" class="pagination pagination-centered">
<ul>
<li t-attf-class="prev #{'active' if page == 1 else '' }"> <a t-att-href=" '%s,%s-%s' % (kanban_url, column_id.id, (page &gt; 1 and page-1 or 1)) ">Prev</a></li>
<t t-foreach="range(page_start, page_end+1)" t-as="p">
<li t-att-class=" 'active' if page == p else '' "> <a t-att-href=" '%s,%s-%s' % (kanban_url, column_id.id, p)" t-esc="p"></a></li>
</t>
<li t-attf-class="next #{'active' if page == page_end else '' }"> <a t-att-href=" '%s,%s-%s' % (kanban_url, column_id.id, (page &lt; page_end and page+1 or page_end) )">Next</a></li>
</ul>
</div>
</td>
</t>
</tr>
</tbody>
</table>
</template>
<!-- Error and special pages -->
<template id="default_page">
<t t-call="website.layout">
<div id="wrap" class="oe_structure oe_empty"></div>
</t>
</template>
<template id="404">
<t t-call="website.layout">
<div id="wrap">
@ -271,6 +371,28 @@
</t>
</template>
<template id="robots">
# robotstxt.org/
User-agent: *
Sitemap: <t t-esc="url_root"/>sitemap.xml
</template>
<template id="sitemap">
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<t t-foreach="pages" t-as="page">
<url t-esc="page['url']"/>
</t>
</urlset>
</template>
<!-- Actual pages -->
<template id="homepage" name="Homepage" page="True">
<t t-call="website.layout">
<div id="wrap" class="oe_structure oe_empty"></div>
</t>
</template>
<template id="company_description" name="Company Description">
<address>
<strong t-field="res_company.name">Name</strong><br />
@ -305,7 +427,7 @@
<a t-attf-href="mailto:{{ res_company.email }}" class="btn btn-primary">Send us an email</a>
</div>
</div>
<div class="col-md-4">
<div class="col-md-4 mb32">
<t t-call="website.company_description"/>
</div>
</div>
@ -347,106 +469,5 @@
</t>
</template>
<template id="pager">
<ul t-if="pager['page_count'] > 1" t-attf-class="#{ classname or '' } pagination">
<li t-att-class=" 'disabled' if pager['page']['num'] == 1 else '' ">
<a t-att-href=" pager['page_start']['url'] if pager['page']['num'] != 1 else '' ">Prev</a>
</li>
<t t-foreach="pager['pages']" t-as="page">
<li t-att-class=" 'active' if page['num'] == pager['page']['num'] else '' "> <a t-att-href="page['url']" t-raw="page['num']"></a></li>
</t>
<li t-att-class=" 'disabled' if pager['page']['num'] == pager['page_count'] else '' ">
<a t-att-href=" pager['page_end']['url'] if pager['page']['num'] != pager['page_count'] else '' ">Next</a>
</li>
</ul>
</template>
<template id="publish_management">
<t t-if="editable" t-ignore="true">
<div class="pull-right btn-group">
<div t-attf-class="btn-group dropdown js_publish_management #{object.id and object.website_published and 'css_publish' or 'css_unpublish'}" t-att-data-id="object.id" t-att-data-object="object._name">
<a t-attf-class="btn btn-xs btn-#{object.id and object.website_published and 'success' or 'default'}" t-att-id="'dopprod-%s' % object.id" role="button" data-toggle="dropdown">Options <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu" t-att-aria-labelledby="'dopprod-%s' % object.id">
<t t-raw="0"/>
<li>
<a href="#" class="js_publish_btn css_unpublish">Unpublish</a>
<a href="#" class="js_publish_btn css_publish">Publish</a>
</li>
</ul>
</div>
<t t-if="publish_edit">
<a class="btn btn-xs btn-default" title="Edit in backend" t-att-href="'/admin/#model=%s&amp;id=%s' % (object._name, object.id)"><span t-attf-class="glyphicon glyphicon-wrench"></span></a>
</t>
</div>
</t>
</template>
<template id="publish_short">
<t t-if="editable" t-ignore="true">
<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">
<span t-attf-class="text-success css_publish glyphicon glyphicon-ok"></span>
<span t-attf-class="text-danger css_unpublish glyphicon glyphicon-remove-circle"></span>
<span t-attf-class="text-muted css_published glyphicon glyphicon-ok"></span>
<span t-attf-class="text-muted css_unpublished glyphicon glyphicon-remove-circle"></span>
</a>
</t>
</template>
<template id="kanban">
<t t-set="step"><t t-esc="step or 0"/></t>
<t t-set="scope"><t t-esc="scope or 0"/></t>
<t t-set="orderby"><t t-esc="orderby or 'name'"/></t>
<t t-raw="website.kanban(model, domain, column, template, step=step, scope=scope, orderby=orderby)"/>
</template>
<template id="kanban_contain">
<table class="table js_kanban">
<thead>
<tr>
<t t-set="width" t-value="str(round(100.0 / len(objects), 2)) + '%'"/>
<t t-foreach="objects">
<th t-att-width="width">
<div t-field="column_id.name" class="text-center"></div>
</th>
</t>
</tr>
</thead>
<tbody>
<tr>
<t t-foreach="objects">
<td class="js_kanban_col" t-att-data-template="template" t-att-data-domain="domain" t-att-data-page_count="page_count" t-att-data-model="model" t-att-data-step="step" t-att-data-orderby="orderby">
<t t-foreach="object_ids" t-as="object_id">
<t t-call="#{ template }"></t>
</t>
<!-- pager -->
<div t-if="1 != page_end" class="pagination pagination-centered">
<ul>
<li t-attf-class="prev #{'active' if page == 1 else '' }"> <a t-att-href=" '%s,%s-%s' % (kanban_url, column_id.id, (page &gt; 1 and page-1 or 1)) ">Prev</a></li>
<t t-foreach="range(page_start, page_end+1)" t-as="p">
<li t-att-class=" 'active' if page == p else '' "> <a t-att-href=" '%s,%s-%s' % (kanban_url, column_id.id, p)" t-esc="p"></a></li>
</t>
<li t-attf-class="next #{'active' if page == page_end else '' }"> <a t-att-href=" '%s,%s-%s' % (kanban_url, column_id.id, (page &lt; page_end and page+1 or page_end) )">Next</a></li>
</ul>
</div>
</td>
</t>
</tr>
</tbody>
</table>
</template>
<template id="robots">
# robotstxt.org/
User-agent: *
Sitemap: <t t-esc="url_root"/>sitemap.xml
</template>
<template id="sitemap">
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<t t-foreach="pages" t-as="page">
<url t-esc="page['url']"/>
</t>
</urlset>
</template>
</data>
</openerp>

View File

@ -55,9 +55,9 @@
<field name="social_youtube" placeholder="http://www.youtube.com/channel/HCU842OHPPNrQ"/>
<field name="social_github" placeholder="https://youraccount.github.io"/>
</group>
<group string="Others Info">
<group string="Other Info">
<field name="company_id" groups="base.group_multi_company"/>
<field name="language_ids" widget="many2many_tags"/>
<field name="language_ids" widget="many2many_checkboxes"/>
<field name="default_lang_id" widget="selection"/>
</group>
</group>
@ -65,5 +65,15 @@
</form>
</field>
</record>
<record id="view_base_language_install" model="ir.ui.view">
<field name="name">view_base_language_install.inherit</field>
<field name="model">base.language.install</field>
<field name="inherit_id" ref="base.view_base_language_install"/>
<field name="arch" type="xml">
<group states="init" position="inside">
<field name="website_ids" widget="many2many_checkboxes"/>
</group>
</field>
</record>
</data>
</openerp>

View File

@ -20,5 +20,5 @@
##############################################################################
import controllers
import website_blog
import models
import wizard

View File

@ -32,19 +32,19 @@ OpenERP Blog
'author': 'OpenERP SA',
'depends': ['knowledge', 'website_mail'],
'data': [
'website_blog_data.xml',
'views/website_blog_classic.xml',
'data/website_blog_data.xml',
'views/website_blog_views.xml',
'views/website_blog_templates.xml',
# 'wizard/document_page_create_menu_view.xml',
'wizard/document_page_show_diff_view.xml',
'security/ir.model.access.csv',
'security/website_mail.xml',
'security/website_blog.xml',
],
'demo': [
'website_blog_demo.xml'
'data/website_blog_demo.xml'
],
'test': [
'test/document_page_test00.yml'
'tests/test_website_blog.yml'
],
'qweb': [
'static/src/xml/*.xml'

View File

@ -35,10 +35,10 @@ class WebsiteBlog(http.Controller):
@website.route([
'/blog/',
'/blog/<int:category_id>/',
'/blog/<int:category_id>/<int:blog_post_id>/',
'/blog/<int:category_id>/page/<int:page>/',
'/blog/<int:category_id>/<int:blog_post_id>/page/<int:page>/',
'/blog/<int:blog_post_id>/',
'/blog/<int:blog_post_id>/page/<int:page>/',
'/blog/cat/<int:category_id>/',
'/blog/cat/<int:category_id>/page/<int:page>/',
'/blog/tag/',
'/blog/tag/<int:tag_id>/',
], type='http', auth="public")
@ -90,11 +90,13 @@ class WebsiteBlog(http.Controller):
tag = tag_obj.browse(cr, uid, tag_id, context=context)
if category_id:
category = category_obj.browse(cr, uid, category_id, context=context)
if category and blog_post_id:
elif blog_post_id:
blog_post = blog_post_obj.browse(cr, uid, blog_post_id, context=context)
blog_message_ids = blog_post.website_message_ids
else:
category = blog_post.category_id
category_id = category.id
if not blog_post_id:
if category and tag:
blog_posts = [cat_post for cat_post in category.blog_post_ids
if tag_id in [post_tag.id for post_tag in cat_post.tag_ids]]
@ -108,7 +110,7 @@ class WebsiteBlog(http.Controller):
if blog_posts:
pager = request.website.pager(
url="/blog/%s/" % category_id,
url="/blog/cat/%s/" % category_id,
total=len(blog_posts),
page=page,
step=self._category_post_per_page,
@ -120,7 +122,7 @@ class WebsiteBlog(http.Controller):
if blog_post:
pager = request.website.pager(
url="/blog/%s/%s/" % (category_id, blog_post_id),
url="/blog/%s/" % blog_post_id,
total=len(blog_message_ids),
page=page,
step=self._post_comment_per_page,
@ -138,8 +140,8 @@ class WebsiteBlog(http.Controller):
nav[year]['months'].append(group)
values = {
'categories': categories,
'category': category,
'categories': categories,
'tag': tag,
'blog_post': blog_post,
'blog_posts': blog_posts,
@ -170,13 +172,13 @@ class WebsiteBlog(http.Controller):
]
return simplejson.dumps(blog_post_data)
@website.route(['/blog/<int:category_id>/<int:blog_post_id>/post'], type='http', auth="public")
def blog_comment(self, category_id=None, blog_post_id=None, **post):
@website.route(['/blog/<int:blog_post_id>/post'], type='http', auth="public")
def blog_post_comment(self, blog_post_id=None, **post):
cr, uid, context = request.cr, request.uid, request.context
url = request.httprequest.host_url
request.session.body = post.get('body')
if request.context['is_public_user']: # purpose of this ?
return '%s/admin#action=redirect&url=%s/blog/%s/%s/post' % (url, url, category_id, blog_post_id)
return '%s/admin#action=redirect&url=%s/blog/%s/post' % (url, url, blog_post_id)
if request.session.get('body') and blog_post_id:
request.registry['blog.post'].message_post(
@ -187,17 +189,24 @@ class WebsiteBlog(http.Controller):
context=dict(context, mail_create_nosubcribe=True))
request.session.body = False
return werkzeug.utils.redirect("/blog/%s/%s/?unable_editor=1" % (category_id, blog_post_id))
return werkzeug.utils.redirect("/blog/%s/?unable_editor=1" % (blog_post_id))
@website.route(['/blog/<int:category_id>/new'], type='http', auth="public")
def create_blog_post(self, category_id=None, **post):
def blog_post_create(self, category_id=None, **post):
cr, uid, context = request.cr, request.uid, request.context
create_context = dict(context, mail_create_nosubscribe=True)
blog_post_id = request.registry['blog.post'].create(
new_blog_post_id = request.registry['blog.post'].create(
request.cr, request.uid, {
'category_id': category_id,
'name': _("Blog title"),
'content': '',
'website_published': False,
}, context=create_context)
return werkzeug.utils.redirect("/blog/%s/%s/?unable_editor=1" % (category_id, blog_post_id))
return werkzeug.utils.redirect("/blog/%s/?unable_editor=1" % (new_blog_post_id))
@website.route(['/blog/<int:blog_post_id>/duplicate'], type='http', auth="public")
def blog_post_copy(self, blog_post_id=None, **post):
cr, uid, context = request.cr, request.uid, request.context
create_context = dict(context, mail_create_nosubscribe=True)
new_blog_post_id = request.registry['blog.post'].copy(cr, uid, blog_post_id, {}, context=create_context)
return werkzeug.utils.redirect("/blog/%s/?unable_editor=1" % (new_blog_post_id))

View File

@ -3,6 +3,7 @@
<!-- <data noupdate="1"> -->
<data>
<!-- jump to blog at install -->
<record id="action_open_website" model="ir.actions.act_url">
<field name="name">Website Blogs</field>
<field name="target">self</field>
@ -17,15 +18,6 @@
<record id="blog_category_1" model="blog.category">
<field name="name">News</field>
<field name="description">Presentation of new OpenERP features</field>
<field name="template">
Summary of the feature
Long explanation
Conclusion
Additional ressources
</field>
</record>
<!-- Post-related subtypes for messaging / Chatter -->
@ -41,6 +33,7 @@ Additional ressources
<field name="default" eval="False"/>
<field name="description">Post Published</field>
</record>
<!-- Project-related subtypes for messaging / Chatter -->
<record id="mt_blog_category_post_new" model="mail.message.subtype">
<field name="name">New Post</field>
@ -51,20 +44,11 @@ Additional ressources
</record>
<!-- Mail group for the company's jobs -->
<record id="blog_category_2" model="blog.category">
<field name="name">Jobs</field>
<field name="description">Job Announces</field>
</record>
<!-- Layout add nav and footer -->
<template id="footer_custom" inherit_option_id="website.layout" name="Job Announces">
<xpath expr="//footer//div[@name='info']/ul" position="inside">
<li><a t-href="/blog/%(website_blog.blog_category_2)d/">Jobs</a></li>
</xpath>
</template>
<!-- Mail group for the company's jobs -->
<record id="blog_category_2" model="blog.category">
<field name="name">Jobs</field>
<field name="description">Job Announces</field>
</record>
</data>

View File

@ -8,6 +8,12 @@
<field name="name">functional</field>
</record>
<record id="blog_tag_2" model="blog.tag">
<field name="name">technical</field>
</record>
<record id="blog_tag_3" model="blog.tag">
<field name="name">website</field>
</record>
<record id="blog_tag_4" model="blog.tag">
<field name="name">pos</field>
</record>
@ -16,6 +22,7 @@
<field name="name">OpenERP v8 New Features</field>
<field name="category_id" ref="blog_category_1"/>
<field name="tag_ids" eval="[(6, 0, [ref('blog_tag_1')])]"/>
<field name="website_published" eval="True"/>
<field name="content"><![CDATA[
<section data-snippet-id='image-text'>
<div class="container">
@ -264,6 +271,7 @@ Think of it as an out-of-the-box solution to boost your business' productivity.
<field name="name">Announcig a New Partnership</field>
<field name="category_id" ref="blog_category_1"/>
<field name="tag_ids" eval="[(6, 0, [ref('blog_tag_1')])]"/>
<field name="website_published" eval="True"/>
<field name="content"><![CDATA[
<section data-snippet-id='image-text'>
<div class="container">
@ -291,7 +299,7 @@ Think of it as an out-of-the-box solution to boost your business' productivity.
<div class="row">
<div class="col-md-12 text-center mt16 mb32">
<h2>
Linked with Project Management
OpenERP Project Management
</h2>
<h3 class="text-muted">Infinitely flexible. Incredibly easy to use.</h3>
</div>
@ -313,33 +321,6 @@ Think of it as an out-of-the-box solution to boost your business' productivity.
</div>
</div>
</section>
<section class="dark mt16 mb16" data-snippet-id='big-picture'>
<div class="container">
<div class="row">
<div class="col-md-12 text-center mt32 mb32">
<h2>Work with the hardware you already have...</h2>
</div>
<div class="col-md-12">
<img class="img-responsive" src="/website/static/src/img/big_picture.png" style="margin: 0 auto;"/>
</div>
<div class="col-md-6 col-md-offset-3 mb16 mt16">
<p class="text-center">
<b>No installation required</b>
</p>
<p class="text-center">
OpenERP's Point of Sale introduces a super clean
interface with no installation required that runs
online and offline on modern hardware. Laptops,
tablets, industrial POS, it runs on everything.
</p>
<p class="text-center">
<a href="/page/website.contactus">Get more information »</a>
</p>
</div>
</div>
</div>
</section>
]]>
</field>
</record>

View File

@ -0,0 +1 @@
import website_blog

View File

@ -37,7 +37,6 @@ class BlogCategory(osv.Model):
_columns = {
'name': fields.char('Name', required=True),
'description': fields.text('Description'),
'template': fields.html('Template'),
'blog_post_ids': fields.one2many(
'blog.post', 'category_id',
'Blogs',
@ -62,7 +61,7 @@ class BlogPost(osv.Model):
_name = "blog.post"
_description = "Blog Post"
_inherit = ['mail.thread']
_order = 'name'
_order = 'write_date DESC'
# maximum number of characters to display in summary
_shorten_max_char = 250
@ -170,6 +169,16 @@ class BlogPost(osv.Model):
self.create_history(cr, uid, ids, vals, context)
return result
def copy(self, cr, uid, id, default=None, context=None):
if default is None:
default = {}
default.update({
'website_message_ids': [],
'website_published': False,
'website_published_datetime': False,
})
return super(BlogPost, self).copy(cr, uid, id, default=default, context=context)
def img(self, cr, uid, ids, field='image_small', context=None):
post = self.browse(cr, SUPERUSER_ID, ids[0], context=context)
return "/website/image?model=%s&field=%s&id=%s" % ('res.users', field, post.create_uid.id)
@ -177,7 +186,7 @@ class BlogPost(osv.Model):
class BlogPostHistory(osv.Model):
_name = "blog.post.history"
_description = "Document Page History"
_description = "Blog Post History"
_order = 'id DESC'
_rec_name = "create_date"

View File

@ -19,11 +19,11 @@
#
##############################################################################
from openerp.addons.mail.tests.test_mail_base import TestMailBase
from openerp.addons.mail.tests.common import TestMail
from openerp.tools import mute_logger, email_split
class TestControllers(TestMailBase):
class TestControllers(TestMail):
def test_00(self):
cr, uid = self.cr, self.uid

View File

@ -1,29 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<!-- Layout add nav and footer -->
<template id="header_footer_custom" inherit_id="website.layout">
<xpath expr="//header//ul[@id='top_menu']/li[@name='contactus']" position="before">
<li><a href="/blog/%(website_blog.blog_category_1)d/">News</a></li>
<li><a t-attf-href="/blog/cat/%(website_blog.blog_category_1)d/">News</a></li>
</xpath>
<xpath expr="//footer//div[@name='info']/ul" position="inside">
<li><a href="/blog/%(website_blog.blog_category_1)d/">News</a></li>
<li><a t-attf-href="/blog/cat/%(website_blog.blog_category_1)d/">News</a></li>
</xpath>
</template>
<template id="footer_custom" inherit_option_id="website.layout" name="Job Announces">
<xpath expr="//footer//div[@name='info']/ul" position="inside">
<li><a t-href="/blog/%(website_blog.blog_category_2)d/">Jobs</a></li>
</xpath>
</template>
<!-- Blog Post Summary -->
<template id="view_blog_post_short" name="Blog Post Summary">
<template id="blog_post_short" name="Blog Post Summary">
<div>
<div class="pull-right">
<t t-call="website.publish_management">
<t t-set="object" t-value="blog_post"/>
<t t-set="publish_edit" t-value="True"/>
<t t-set="publish_duplicate" t-value="'/blog/%s/duplicate' % (blog_post.id)"/>
</t>
</div>
<h2 class="text-center">
<a t-attf-href="/blog/#{blog_post.category_id.id}/#{blog_post.id}" t-field="blog_post.name"></a>
<a t-attf-href="/blog/#{blog_post.id}" t-field="blog_post.name"></a>
</h2>
<p class="post-meta text-muted text-center">
<p class="post-meta text-muted text-center" name='blog_post_data'>
<span class="icon-calendar"> <span t-field="blog_post.create_date"/></span> &amp;nbsp;
<span class="icon-user"> By <span t-field="blog_post.create_uid"/> &amp;nbsp;</span>
<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 t-if="len(blog_post.message_ids) &gt; 0" class="icon-comment">
<a t-attf-href="/blog/#{blog_post.id}/#comment">
<t t-if="len(blog_post.message_ids) &lt;= 1" ><t t-esc="len(blog_post.message_ids)"/> comment</t>
<t t-if="len(blog_post.message_ids) > 1"><t t-esc="len(blog_post.message_ids)"/> comments</t>
</a>
</span>
</p>
<div t-field="blog_post.shortened_content" class="blog_content"/>
@ -33,43 +47,56 @@
</template>
<!-- Options: Blog Post Summary: hide author -->
<template id="view_blog_post_short_author" inherit_option_id="website_blog.view_blog_post_short" name="Hide Authors">
<template id="opt_blog_post_short_author" name="Hide Authors"
inherit_option_id="website_blog.blog_post_short">
<xpath expr="//span[@class='icon-user']" position="attributes">
<attribute name="class">hidden</attribute>
</xpath>
</template>
<!-- Options: Blog Post Summary: show category -->
<template id="view_blog_post_short_category" inherit_option_id="website_blog.view_blog_post_short" name="Show Blog Name">
<template id="opt_blog_post_short_category" name="Blog Name"
inherit_option_id="website_blog.blog_post_short">
<xpath expr="//span[@class='icon-user']" position="after">
<span class="icon-folder-open"> In <span t-field="blog_post.category_id"/> &amp;nbsp;</span>
</xpath>
</template>
<!-- Option: Blog Post Summary: show tags -->
<template id="opt_blog_post_short_tags" name="Tags"
inherit_option_id="website_blog.blog_post_short" inherit_id="website_blog.blog_post_short">
<xpath expr="//p[@name='blog_post_data']" position="after">
<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 t-attf-href="/blog/tag/#{tag.id}" t-esc="tag.name"/> &amp;nbsp;
</t>
</p>
</xpath>
</template>
<!-- Blog Post Complete -->
<template id="view_blog_post" name="Blog Post">
<template id="blog_post_complete" name="Blog Post">
<div>
<t t-call="website.publish_management">
<t t-set="object" t-value="blog_post"/>
<t t-set="publish_edit" t-value="True"/>
<t t-set="publish_duplicate" t-value="'/blog/%s/duplicate' % (blog_post.id)"/>
</t>
</div><div class="clearfix"/>
</div>
<div class="clearfix"/>
<h1 class="text-center" t-field="blog_post.name"/>
<p class="post-meta text-muted text-center">
<p class="post-meta text-muted text-center" name="blog_post_data">
<span class="icon-calendar"> <span t-field="blog_post.create_date"/></span> &amp;nbsp;
<span class="icon-user"> By <span t-field="blog_post.create_uid"/> &amp;nbsp;</span>
<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>
<a t-attf-href="#comments">
<t t-if="len(blog_post.message_ids) &lt;= 1" ><t t-esc="len(blog_post.message_ids)"/> comment</t>
<t t-if="len(blog_post.message_ids) > 1"><t t-esc="len(blog_post.message_ids)"/> comments</t>
</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 t-attf-href="/blog/tag/#{tag.id}" t-esc="tag.name"/> &amp;nbsp;
</t>
</p>
<div t-field="blog_post.content" class="mt16"/>
<hr class="mb32"/>
@ -102,78 +129,103 @@
</template>
<!-- Options: Blog Post: user can reply -->
<template id="view_blog_post_author" inherit_option_id="website_blog.view_blog_post" name="Hide Authors">
<template id="opt_blog_post_complete_comment" name="Comment Form"
inherit_option_id="website_blog.blog_post_complete" inherit_id="website_blog.blog_post_complete">
<xpath expr="//ul[last()]" position="after">
<section groups="group_website_blog_reply" class="mb32">
<h4>Leave a Comment</h4>
<form id="comment" t-attf-action="/blog/#{blog_post.category_id.id}/#{blog_post.id}/post#post"
method="POST">
<img class="img pull-left img-rounded" t-att-src="'/website/image?model=res.partner&amp;field=image_small&amp;id='+str(user_id.partner_id.id)" style="width: 50px; margin-right: 10px;"/>
<div class="pull-left mb32" style="width: 75%%">
<textarea rows="3" class="form-control" placeholder="Write a comment..."></textarea>
<button type="submit" class="btn btn-danger mt8">Post</button>
</div>
</form>
</section>
<section groups="group_website_blog_reply" class="mb32">
<h4>Leave a Comment</h4>
<form id="comment" t-attf-action="/blog/#{blog_post.id}/post#post"
method="POST">
<img class="img pull-left img-rounded" t-att-src="'/website/image?model=res.partner&amp;field=image_small&amp;id='+str(user_id.partner_id.id)" style="width: 50px; margin-right: 10px;"/>
<div class="pull-left mb32" style="width: 75%%">
<textarea rows="3" class="form-control" placeholder="Write a comment..."></textarea>
<button type="submit" class="btn btn-danger mt8">Post</button>
</div>
</form>
</section>
</xpath>
</template>
<!-- Options: Blog Post: hide author -->
<template id="view_blog_post_author" inherit_option_id="website_blog.view_blog_post" name="Hide Authors">
<template id="opt_blog_post_complete_author" name="Hide Authors"
inherit_option_id="website_blog.blog_post_complete">
<xpath expr="//span[@class='icon-user']" position="attributes">
<attribute name="class">hidden</attribute>
</xpath>
</template>
<!-- Options: Blog Post: show category -->
<template id="view_blog_post_show_category" inherit_option_id="website_blog.view_blog_post" name="Show Blog Name">
<template id="opt_blog_post_complete_category" name="Blog Name"
inherit_option_id="website_blog.blog_post_complete">
<xpath expr="//span[@class='icon-user']" position="after">
<span class="icon-folder-open"> In <span t-field="blog_post.category_id"/> &amp;nbsp;</span>
</xpath>
</template>
<!-- Options: Blog Post: show tags -->
<template id="opt_blog_post_complete_tags" name="Tags"
inherit_option_id="website_blog.blog_post_complete" inherit_id="website_blog.blog_post_complete">
<xpath expr="//p[@name='blog_post_data']" position="after">
<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 t-attf-href="/blog/tag/#{tag.id}" t-esc="tag.name"/> &amp;nbsp;
</t>
</p>
</xpath>
</template>
<!-- Page -->
<template id="index" name="Blogs" page="True">
<t t-call="website.layout">
<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 t-set="title">Blog</t>
<div id="wrap">
<div class="container mt16 js_website_blog">
<div class="row">
<div class="col-lg-8 col-sm-9" t-if="not blog_post">
<t t-foreach="blog_posts" t-as="blog_post" data-publish="">
<t t-call="website_blog.view_blog_post_short"/>
</t>
<div class="pull-right" t-call="website.pager"/>
</div>
<div class="col-md-9 col-lg-8" t-if="blog_post">
<t t-call="website_blog.view_blog_post">
<t t-set="blog_post" t-value="blog_post"/>
</t>
<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 t-set="title">Blog</t>
<div id="wrap">
<div class="container mt16 js_website_blog">
<div class="row">
<div class="col-lg-12 col-sm-12" t-if="not blog_post" id="blog_post">
<t t-if="category and editable">
<div class="row">
<a t-attf-href="/blog/#{category.id}/new" class="btn btn-primary pull-right">New Blog Post</a>
</div>
</t>
<t t-foreach="blog_posts" t-as="blog_post" data-publish="">
<t t-call="website_blog.blog_post_short"/>
</t>
<div class="pull-right" t-call="website.pager"/>
</div>
<div class="col-lg-12 col-sm-12" t-if="blog_post" id="blog_posts_summary">
<t t-call="website_blog.blog_post_complete">
<t t-set="blog_post" t-value="blog_post"/>
</t>
</div>
</div>
</div>
</div>
</div>
</t>
</template>
<!-- Option: right column -->
<template id="blog_right_column" inherit_id="website_blog.index" inherit_option_id="website_blog.index" name="Right Column">
<!-- Option: Blog: Right Column -->
<template id="blog_right_column" name="Right Column"
inherit_option_id="website_blog.index" inherit_id="website_blog.index">
<xpath expr="//div[@class='row']" position="inside">
<div class="col-sm-3 hidden-xs col-lg-offset-1 mb32" id="right_column">
<t t-if="category and editable">
<a t-attf-href="/blog/#{category.id}/new" class="btn btn-primary pull-right">New Blog Post</a>
</t>
<div class="clearfix mb48"/>
<div class="col-lg-3 col-sm-3 hidden-xs col-lg-offset-1 mb32" id="right_column">
</div>
</xpath>
<xpath expr="//div[@id='blog_post']" position="attributes">
<attribute name="class">col-lg-8 col-sm-8</attribute>
</xpath>
<xpath expr="//div[@id='blog_posts_summary']" position="attributes">
<attribute name="class">col-lg-8 col-sm-8</attribute>
</xpath>
</template>
<!-- Option: Right Column: archives -->
<template id="blog_history" inherit_id="website_blog.blog_right_column" inherit_option_id="website_blog.blog_right_column" name="Archives">
<template id="opt_blog_rc_history" name="Archives"
inherit_option_id="website_blog.blog_right_column" inherit_id="website_blog.blog_right_column">
<xpath expr="//div[@id='right_column']" position="inside">
<section>
<h4>Archives</h4>
@ -195,7 +247,8 @@
</template>
<!-- Option: Right Column: about us -->
<template id="blog_aboutus" inherit_option_id="website_blog.blog_right_column" inherit_id="website_blog.blog_right_column" name="About" priority="4">
<template id="opt_blog_rc_about_us" name="About Us" priority="4"
inherit_option_id="website_blog.blog_right_column" inherit_id="website_blog.blog_right_column">
<xpath expr="//div[@id='right_column']" position="inside">
<section class="mb16">
<h4>About us</h4>
@ -211,7 +264,8 @@
</template>
<!-- Option: Right Column: follow us -->
<template id="blog_followus" inherit_option_id="website_blog.blog_right_column" inherit_id="website_blog.blog_right_column" name="Follow us" priority="2">
<template id="opt_blog_rc_follow_us" name="Follow us" priority="2"
inherit_option_id="website_blog.blog_right_column" inherit_id="website_blog.blog_right_column">
<xpath expr="//div[@id='right_column']" position="inside">
<section class="mb16">
<h4>Follow us</h4>
@ -239,8 +293,9 @@
</xpath>
</template>
<!-- Option: Right Column: categories + category in blog post -->
<template id="blog_categories" inherit_option_id="website_blog.blog_right_column" name="Other Blogs" priority="6">
<!-- Option: Right Column: categories -->
<template id="opt_blog_rc_categories" name="Other Blogs" priority="6"
inherit_option_id="website_blog.blog_right_column">
<xpath expr="//div[@id='right_column']" position="inside">
<section class="mb16">
<h4>Other Blogs</h4>
@ -257,5 +312,5 @@
</xpath>
</template>
</data>
</data>
</openerp>

View File

@ -2,7 +2,7 @@
<openerp>
<data>
<menuitem name="Knowledge" id="knowledge.menu_document"/>
<menuitem name="Pages" id="menu_wiki" parent="knowledge.menu_document" sequence="20" />
<menuitem name="Blog Posts" id="menu_wiki" parent="knowledge.menu_document" sequence="20" />
<!-- Category views -->
<record model="ir.ui.view" id="view_blog_category_list">
@ -23,7 +23,6 @@
<group>
<field name="name"/>
<field name="description"/>
<field name="template" widget="html"/>
</group>
</sheet>
<div class="oe_chatter">
@ -60,14 +59,12 @@
<group>
<field name="category_id" string="Category"/>
</group>
<group>
<field name="write_uid" groups="base.group_no_one" context="{'default_groups_ref': ['base.group_user', 'base.group_partner_manager', 'base.group_document_user']}"/>
<field name="write_date" groups="base.group_no_one"/>
<field name="menu_id" groups="base.group_no_one"/>
<field name="content" placeholder="e.g. Once upon a time..." widget="html"/>
<group string="Technical" groups="base.group_no_one">
<field name="write_uid" context="{'default_groups_ref': ['base.group_user', 'base.group_partner_manager', 'base.group_document_user']}"/>
<field name="write_date"/>
<field name="menu_id"/>
</group>
<div class="oe_document_page">
<field name="content" placeholder="e.g. Once upon a time..." widget="html"/>
</div>
</sheet>
<div class="oe_chatter">
<field name="message_follower_ids" widget="mail_followers" groups="base.group_user"/>
@ -107,7 +104,7 @@
</p>
</field>
</record>
<menuitem id="menu_page" parent="menu_wiki" name="Pages" action="action_blog_post" sequence="10"/>
<menuitem id="menu_page" parent="menu_wiki" name="Blog Posts" action="action_blog_post" sequence="10"/>
<record model="ir.actions.act_window" id="action_blog_category">
<field name="name">Category</field>

View File

@ -1 +0,0 @@
import controllers

View File

@ -1,18 +0,0 @@
{
'name': 'Public References',
'category': 'Website',
'summary': 'Publish Customer References',
'version': '1.0',
'description': """
OpenERP Blog
============
""",
'author': 'OpenERP SA',
'depends': ['website_partner', 'sale', 'website_google_map'],
'data': [
'views/website_contract.xml',
],
'qweb': ['static/src/xml/*.xml'],
'installable': True,
}

View File

@ -1,102 +0,0 @@
# -*- coding: utf-8 -*-
import openerp
from openerp.addons.web import http
from openerp.tools.translate import _
from openerp.addons.web.http import request
from openerp.addons.website.models import website
import urllib
class website_contract(http.Controller):
@website.route(['/references/', '/references/page/<int:page>/'], type='http', auth="public")
def references(self, page=0, **post):
partner_obj = request.registry['res.partner']
account_obj = request.registry['account.analytic.account']
# check contracts
contract_ids = account_obj.search(request.cr, openerp.SUPERUSER_ID, [(1, "=", 1)])
contracts = account_obj.browse(request.cr, openerp.SUPERUSER_ID,
contract_ids, request.context)
contract_project_ids = [contract.partner_id.id for contract in contracts
if contract.partner_id]
domain = ['|', ('id', "in", contract_project_ids), ('id', "child_of", contract_project_ids)]
if post.get('search'):
domain += [
'|',
('name', 'ilike', "%%%s%%" % post.get("search")),
('website_description', 'ilike', "%%%s%%" % post.get("search"))
]
if request.context['is_public_user']:
domain = ['&'] + domain + [('website_published', '=', True)]
# public partner profile
partner_ids = partner_obj.search(
request.cr, openerp.SUPERUSER_ID,
domain + [('website_published', '=', True)], context=request.context)
google_map_partner_ids = ",".join([str(p) for p in partner_ids])
# group by country
countries = partner_obj.read_group(
request.cr, request.uid, domain, ["id", "country_id"],
groupby="country_id", orderby="country_id", context=request.context)
country_count = partner_obj.search(
request.cr, request.uid, domain, count=True, context=request.context)
countries.insert(0, {
'country_id_count': country_count,
'country_id': ("all", _("All Countries"))
})
if post.get("country", "all") != 'all':
partner_ids = partner_obj.search(
request.cr, request.uid,
[
('id', 'in', partner_ids),
('country_id', '=', int(post.get('country')))
], context=request.context)
step = 20
pager = request.website.pager(url="/references/", total=len(partner_ids), page=page, step=step, scope=7, url_args=post)
partner_ids = partner_obj.search(
request.cr, openerp.SUPERUSER_ID, [('id', 'in', partner_ids)],
limit=step, offset=pager['offset'], context=request.context)
partners = partner_obj.browse(request.cr, openerp.SUPERUSER_ID,
partner_ids, request.context)
values = {
'countries': countries,
'partner_ids': partners,
'google_map_partner_ids': google_map_partner_ids,
'pager': pager,
'searches': post,
'search_path': "?%s" % urllib.urlencode(post),
}
return request.website.render("website_contract.index", values)
@website.route(['/references/<int:partner_id>/'], type='http', auth="public")
def references_ref(self, partner_id=None, **post):
""" Route for displaying a single partner.
:param integer partner_id: partner to display. If not set or not valid
call basic references method.
"""
partner_obj = request.registry['res.partner']
if request.context['is_public_user']:
partner_ids = partner_obj.search(
request.cr, openerp.SUPERUSER_ID, [
('website_published', '=', True),
('id', '=', partner_id)
], context=request.context)
partner_id = partner_ids and partner_ids[0] or None
if not partner_id:
return self.references(post=post)
values = {
'partner_id': partner_obj.browse(
request.cr, openerp.SUPERUSER_ID, partner_id,
dict(request.context, show_address=True)),
}
return request.website.render("website_contract.details", values)

View File

@ -1,12 +1,11 @@
{
'name': 'Public Partners References',
'name': 'Publish Partner Assignment',
'category': 'Website',
'summary': 'Publish Customer References',
'summary': 'Publish and Assign Partner',
'version': '1.0',
'description': """
OpenERP Public Partners References
==================================
Publish and Assign Partner
==========================
""",
'author': 'OpenERP SA',
'depends': ['website_partner', 'website_google_map'],

View File

@ -1,82 +1,88 @@
# -*- coding: utf-8 -*-
import urllib
import openerp
from openerp.addons.web import http
from openerp.tools.translate import _
from openerp.addons.web.http import request
from openerp.addons.website.models import website
import urllib
class website_crm_partner_assign(http.Controller):
@website.route(['/partners/', '/partners/page/<int:page>/'], type='http', auth="public", multilang=True)
def partners(self, page=0, **post):
class WebsiteCrmPartnerAssign(http.Controller):
_references_per_page = 20
@website.route([
'/partners/', '/partners/page/<int:page>/',
'/partners/country/<int:country_id>', '/partners/country/page/<int:country_id>/',
], type='http', auth="public", multilang=True)
def partners(self, country_id=0, page=0, **post):
country_obj = request.registry['res.country']
partner_obj = request.registry['res.partner']
post_name = post.get('search', '')
grade_id = post.get('grade', '')
country = None
def dom_without(without):
domain = [('grade_id', '!=', False)]
domain += openerp.SUPERUSER_ID != request.uid and [('website_published', '=', True)] or [(1, "=", 1)]
for key, search in domain_search.items():
if key != without:
domain += search
return domain
# format displayed membership lines domain
base_partner_domain = [('is_company', '=', True)]
if request.context['is_public_user']:
base_partner_domain += [('website_published', '=', True)]
partner_domain = list(base_partner_domain)
if grade_id:
partner_domain += [('grade_id', '=', int(grade_id))] # try/catch int
if country_id:
country = country_obj.browse(request.cr, request.uid, country_id, request.context)
partner_domain += [('country_id', '=', country_id)]
if post_name:
partner_domain += ['|', ('name', 'ilike', "%%%s%%" % post_name), ('website_description', 'ilike', "%%%s%%" % post_name)]
# search domains
domain_search = {}
if post.get('search'):
domain_search["search"] += ['|',
('name', 'ilike', "%%%s%%" % post.get("search")),
('website_description', 'ilike', "%%%s%%" % post.get("search"))]
if post.get("grade", "all") != 'all':
domain_search["grade"] = [("grade_id", "=", int(post.get("grade")))]
if post.get("country", "all") != 'all':
domain_search["country"] = [("country_id", "=", int(post.get("country")))]
# public partner profile
# format pager
partner_ids = partner_obj.search(
request.cr, openerp.SUPERUSER_ID, dom_without(False),
request.cr, openerp.SUPERUSER_ID, partner_domain,
context=request.context)
pager = request.website.pager(url="/partners/", total=len(partner_ids), page=page, step=self._references_per_page, scope=7, url_args=post)
# search for partners to display
partner_ids = partner_obj.search(
request.cr, openerp.SUPERUSER_ID, partner_domain,
context=request.context,
limit=self._references_per_page, offset=pager['offset'],
order="grade_id ASC,partner_weight DESC")
google_map_partner_ids = ",".join([str(p) for p in partner_ids])
partners = partner_obj.browse(
request.cr, openerp.SUPERUSER_ID, partner_ids, request.context)
# group by country
domain = dom_without("country")
countries = partner_obj.read_group(
request.cr, request.uid, domain, ["id", "country_id"],
request.cr, request.uid, base_partner_domain, ["id", "country_id"],
groupby="country_id", orderby="country_id", context=request.context)
partners = partner_obj.search(request.cr, request.uid, domain,
context=request.context, count=True)
countries_partners = partner_obj.search(
request.cr, request.uid, base_partner_domain,
context=request.context, count=True)
countries.insert(0, {
'country_id_count': partners,
'country_id': ("all", _("All Countries"))
'country_id_count': countries_partners,
'country_id': (0, _("All Countries"))
})
# group by grade
domain = dom_without("grade")
grades = partner_obj.read_group(
request.cr, request.uid, domain, ["id", "grade_id"],
request.cr, request.uid, base_partner_domain, ["id", "grade_id"],
groupby="grade_id", orderby="grade_id", context=request.context)
grade_id_count = partner_obj.search(request.cr, request.uid, domain,
count=True, context=request.context)
grades_partners = partner_obj.search(
request.cr, request.uid, base_partner_domain,
context=request.context, count=True)
grades.insert(0, {
'grade_id_count': grade_id_count,
'grade_id': ("all", _("All Grade"))
'grade_id_count': grades_partners,
'grade_id': ("all", _("All Grades"))
})
step = 20
pager = request.website.pager(url="/partners/", total=len(partner_ids), page=page, step=step, scope=7, url_args=post)
partner_ids = partner_obj.search(
request.cr, openerp.SUPERUSER_ID, [('id', 'in', partner_ids)],
context=request.context, limit=step, offset=pager['offset'],
order="grade_id ASC,partner_weight DESC")
partners = partner_obj.browse(request.cr, openerp.SUPERUSER_ID,
partner_ids, request.context)
values = {
'countries': countries,
'current_country_id': country_id,
'current_country': country,
'grades': grades,
'partner_ids': partners,
'grade_id': grade_id,
'partners': partners,
'google_map_partner_ids': google_map_partner_ids,
'pager': pager,
'searches': post,
@ -84,23 +90,19 @@ class website_crm_partner_assign(http.Controller):
}
return request.website.render("website_crm_partner_assign.index", values)
@website.route(['/partners/<int:ref_id>/'], type='http', auth="public", multilang=True)
def partners_ref(self, ref_id=0, **post):
@website.route(['/partners/<int:partner_id>/'], type='http', auth="public", multilang=True)
def partners_ref(self, partner_id=0, **post):
partner_obj = request.registry['res.partner']
partner_ids = partner_obj.search(
request.cr, openerp.SUPERUSER_ID,
[('website_published', '=', True), ('id', '=', ref_id)],
context=request.context)
if not request.context['is_public_user']:
partner_ids += partner_obj.search(
request.cr, request.uid, [('id', '=', ref_id)],
context=request.context)
if request.context['is_public_user']:
partner_ids = partner_obj.search(request.cr, openerp.SUPERUSER_ID, [('website_pushished', '=', True), ('id', '=', partner_id)], context=request.context)
else:
partner_ids = partner_obj.search(request.cr, request.uid, [('id', '=', partner_id)], context=request.context)
if not partner_ids:
return self.members(post)
values = {
'partner_id': partner_obj.browse(
request.cr, openerp.SUPERUSER_ID, partner_ids[0],
context=dict(request.context, show_address=True)),
}
return request.website.render("website_crm_partner_assign.details", values)
return request.website.render("website_crm_partner_assign.partner", values)

View File

@ -4,7 +4,6 @@
<!-- Layout add nav and footer -->
<template id="footer_custom" inherit_id="website.layout" name="Custom Footer">
<xpath expr="//footer//div[@name='info']/ul" position="inside">
<li><a t-href="/partners/">Partners</a></li>
@ -12,29 +11,42 @@
</template>
<!-- Page -->
<template id="layout" name="Partners Layout">
<t t-call="website.layout">
<t t-set="title">Partners</t>
<div id="wrap">
<div class="oe_structure"/>
<div class="container">
<div class="row">
<t t-raw="ref_content" />
<t t-set="title">Partners</t>
<div id="wrap">
<div class="oe_structure"/>
<div class="container">
<div class="row">
<t t-raw="ref_content" />
</div>
</div>
</div>
</div>
</t>
</template>
<template id="index" name="Partners">
<template id="index" name="Find Partners">
<t t-call="website_crm_partner_assign.layout">
<t t-set="ref_content">
<h1 class="col-md-12 text-center">
Want services on OpenERP?<br/>
<small>Contact a local partner</small>
</h1>
<div class="col-md-12" id="ref_content">
<div class="col-md-4" id="partner_left">
<h3>Partners by Country</h3>
<ul class="nav nav-pills nav-stacked mt16">
<t t-foreach="countries" t-as="country_dict">
<t t-if="country_dict['country_id']">
<li t-att-class="country_dict['country_id'][0] == current_country_id and 'active' or ''">
<a t-href="/partners/country/#{ country_dict['country_id'][0] }">
<t t-esc="country_dict['country_id'][1]"/> <small>(<t t-esc="country_dict['country_id_count']"/>)</small>
</a>
</li>
</t>
</t>
</ul>
</div>
<div class="col-md-8" id="ref_content">
<div class='navbar'>
<div>
<t t-call="website.pager">
@ -42,26 +54,15 @@
</t>
<form t-action="/partners/" method="get" class="navbar-search pull-right pagination form-inline">
<div class="form-group">
<input type="text" name="search" class="search-query col-md-2 mt4 form-control" placeholder="Search" t-att-value="searches.get('search') or '' or ''"/>
</div>
<div class="form-group">
<select class="search-query col-md-2 mt4 form-control" name="country" t-if="len(countries) > 1" onchange="submit()">
<t t-foreach="countries">
<t t-if="country_id">
<option t-att-selected="searches.get('country') == str(country_id and country_id[0]) and 'selected'" t-att-value="country_id[0]">
<t t-esc="country_id[1]"/> (<t t-esc="country_id_count"/>)
</option>
</t>
</t>
</select>
<input type="text" name="search" class="search-query col-md-2 mt4 form-control" placeholder="Search" t-att-value="name_search"/>
</div>
<div class="form-group">
<select class="search-query col-md-2 mt4 form-control" name="grade" t-if="len(grades) > 1" onchange="submit()">
<t t-foreach="grades">
<t t-if="grade_id">
<option t-att-selected="searches.get('grade') == str(grade_id and grade_id[0]) and 'selected'" t-att-value="grade_id[0]">
<t t-esc="grade_id[1]"/> (<t t-esc="grade_id_count"/>)
</option>
<option t-att-selected="searches.get('grade') == str(grade_id and grade_id[0]) and 'selected'" t-att-value="grade_id[0]">
<t t-esc="grade_id[1]"/> (<t t-esc="grade_id_count"/>)
</option>
</t>
</t>
</select>
@ -70,19 +71,22 @@
</div>
</div>
<div>
<t t-foreach="partner_ids" t-as="partner">
<t t-if="grade_id != partner.grade_id.id">
<t t-set="grade_id" t-value="partner.grade_id.id"/>
<h3 class="text-center well"><span t-field="partner.grade_id"/> Partners</h3>
<t t-foreach="partners" t-as="partner_id">
<t t-if="internal_gid != partner_id.grade_id.id">
<t t-set="internal_gid" t-value="partner_id.grade_id.id"/>
<h3 class="text-center">
<span t-field="partner_id.grade_id"/> Partners
<t t-if="current_country"> in <t t-esc="current_country.name"/></t>
</h3>
</t>
<div class="media thumbnail" data-publish="">
<t t-call="website.publish_management"><t t-set="object" t-value="partner"/></t>
<a class="pull-left" t-href="/partners/#{ partner.id }/">
<img class="media-object" t-att-src="partner.img('image_small')"/>
<t t-call="website.publish_management"><t t-set="object" t-value="partner_id"/></t>
<a class="pull-left" t-href="/partners/#{ partner_id.id }/">
<img class="media-object" t-att-src="partner_id.img('image_small')"/>
</a>
<div class="media-body" style="min-height: 64px;">
<a class="media-heading" t-href="/partners/#{ partner.id }/"><span t-field="partner.parent_id"/> <span t-field="partner.name"/></a> - <span t-field="partner.grade_id"/>
<div t-field="partner.website_short_description"/>
<a class="media-heading" t-href="/partners/#{ partner_id.id }/"><span t-field="partner_id.parent_id"/> <span t-field="partner_id.name"/></a> - <span t-field="partner_id.grade_id"/>
<div t-field="partner_id.website_short_description"/>
</div>
</div>
</t>
@ -93,48 +97,19 @@
</template>
<template id="ref_country" inherit_id="website_crm_partner_assign.index" inherit_option_id="website_crm_partner_assign.index" name="Left World Map">
<xpath expr="//div[@id='ref_content']" position="before">
<div class="col-md-4">
<ul class="nav">
<li class="navbar-header">World Map</li>
<iframe t-attf-src="/google_map/?width=320&amp;height=240&amp;partner_ids=#{ google_map_partner_ids }&amp;partner_url=/partners/"
style="width:320px; height:260px; border:0; padding:0; margin:0;"></iframe>
</ul>
</div>
</xpath>
<xpath expr="//div[@id='ref_content']" position="attributes">
<attribute name="class">col-md-8</attribute>
<xpath expr="//div[@id='partner_left']//ul" position="after">
<h3>World Map</h3>
<ul class="nav">
<iframe t-attf-src="/google_map/?width=320&amp;height=240&amp;partner_ids=#{ google_map_partner_ids }&amp;partner_url=/partners/"
style="width:320px; height:260px; border:0; padding:0; margin:0;"></iframe>
</ul>
</xpath>
</template>
<template id="details" name="Partner Detail">
<template id="partner" name="Partner Detail">
<t t-call="website_crm_partner_assign.layout">
<t t-set="ref_content">
<t t-call="website.publish_management"><t t-set="object" t-value="partner_id"/></t>
<h1 class="col-md-12 text-center" t-field="partner_id.name"/>
<div class="col-md-4">
<div class="text-center">
<img t-att-src="partner_id.img('image_medium')"/>
</div>
<address>
<table style="margin: auto;" class="well">
<colgroup>
<col width="100"/>
<col/>
</colgroup>
<tbody>
<t t-set="address" t-value="'&lt;br/&gt;'.join(partner_id.name_get()[0][1].split('\n')[1:])"/>
<tr t-if="address or editable"><th>Address</th><td class="span2" t-raw="address"/></tr>
<tr t-if="partner_id.website or editable"><th>Website</th><td class="span2" t-field="partner_id.website"/></tr>
<tr t-if="partner_id.phone or editable"><th>Tel</th><td class="span2" t-field="partner_id.phone"/></tr>
<tr t-if="partner_id.mobile or editable"><th>Tel</th><td class="span2" t-field="partner_id.mobile"/></tr>
<tr t-if="partner_id.fax or editable"><th>Fax</th><td class="span2" t-field="partner_id.fax"/></tr>
<tr t-if="partner_id.email or editable"><th>Email</th><td class="span2" t-field="partner_id.email"/></tr>
</tbody>
</table>
</address>
</div>
<div class="col-md-8 mt32" t-field="partner_id.website_description"/>
<t t-call="website_partner.partner_detail"/>
</t>
</t>
</template>

Some files were not shown because too many files have changed in this diff Show More