[MERGE] : with trunk

bzr revid: aja@tinyerp.com-20140328042815-kv5qmuh2wxdt9wwp
This commit is contained in:
ajay javiya (OpenERP) 2014-03-28 09:58:15 +05:30
commit e63646130e
20 changed files with 550 additions and 7 deletions

View File

@ -0,0 +1,2 @@
import controllers
import models

View File

@ -0,0 +1,21 @@
{
'name': 'Base import module',
'description': """
Import a custom data module
===========================
This module allows authorized users to import a custom data module (.xml files and static assests)
for customization purpose.
""",
'category': 'Uncategorized',
'website': 'http://www.openerp.com',
'author': 'OpenERP SA',
'depends': ['web'],
'installable': True,
'auto_install': False,
'data': [],
'css': [],
'js': [],
'qweb': [],
'test': [],
}

View File

@ -0,0 +1,86 @@
#!/usr/bin/env python
import argparse
import os
import sys
import tempfile
import zipfile
try:
import requests
except ImportError:
# no multipart encoding in stdlib and this script is temporary
sys.exit("This script requires the 'requests' module. ( pip install requests )")
session = requests.session()
def deploy_module(module_path, url, login, password, db=''):
url = url.rstrip('/')
authenticate(url, login, password, db)
module_file = zip_module(module_path)
try:
return upload_module(url, module_file)
finally:
os.remove(module_file)
def upload_module(server, module_file):
print("Uploading module file...")
url = server + '/base_import_module/upload'
files = dict(mod_file=open(module_file, 'rb'))
res = session.post(url, files=files)
if res.status_code != 200:
raise Exception("Could not authenticate on server '%s'" % server)
return res.text
def authenticate(server, login, password, db=''):
print("Authenticating on server '%s' ..." % server)
# Fixate session with a given db if any
session.get(server + '/web/login', params=dict(db=db))
args = dict(login=login, password=password, db=db)
res = session.post(server + '/base_import_module/login', args)
if res.status_code == 404:
raise Exception("The server '%s' does not have the 'base_import_module' installed." % server)
elif res.status_code != 200:
raise Exception(res.text)
def zip_module(path):
path = os.path.abspath(path)
if not os.path.isdir(path):
raise Exception("Could not find module directory '%s'" % path)
container, module_name = os.path.split(path)
temp = tempfile.mktemp(suffix='.zip')
try:
print("Zipping module directory...")
with zipfile.ZipFile(temp, 'w') as zfile:
for root, dirs, files in os.walk(path):
for file in files:
file_path = os.path.join(root, file)
zfile.write(file_path, file_path.split(container).pop())
return temp
except Exception:
os.remove(temp)
raise
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Deploy a module on an OpenERP server.')
parser.add_argument('path', help="Path of the module to deploy")
parser.add_argument('--url', dest='url', help='Url of the server (default=http://localhost:8069)', default="http://localhost:8069")
parser.add_argument('--db', dest='db', help='Database to use if server does not use db-filter.')
parser.add_argument('--login', dest='login', default="admin", help='Login (default=admin)')
parser.add_argument('--password', dest='password', default="admin", help='Password (default=admin)')
parser.add_argument('--no-ssl-check', dest='no_ssl_check', action='store_true', help='Do not check ssl cert')
if len(sys.argv) == 1:
sys.exit(parser.print_help())
args = parser.parse_args()
if args.no_ssl_check:
session.verify = False
try:
result = deploy_module(args.path, args.url, args.login, args.password, args.db)
print(result)
except Exception, e:
sys.exit("ERROR: %s" % e)

View File

@ -0,0 +1 @@
import main

View File

@ -0,0 +1,73 @@
# -*- coding: utf-8 -*-
import functools
import os
import zipfile
from os.path import join as opj
import openerp
from openerp.http import Controller, route, request, Response
MAX_FILE_SIZE = 100 * 1024 * 1024 # in megabytes
def webservice(f):
@functools.wraps(f)
def wrap(*args, **kw):
try:
return f(*args, **kw)
except Exception, e:
return Response(response=str(e), status=500)
return wrap
class ImportModule(Controller):
def check_user(self, uid=None):
if uid is None:
uid = request.uid
is_admin = request.registry['res.users'].has_group(request.cr, uid, 'base.group_erp_manager')
if not is_admin:
raise openerp.exceptions.AccessError("Only administrators can upload a module")
@route('/base_import_module/login', type='http', auth='none', methods=['POST'])
@webservice
def login(self, login, password, db=None):
if db and db != request.db:
raise Exception("Could not select database '%s'" % db)
uid = request.session.authenticate(request.db, login, password)
if not uid:
return Response(response="Wrong login/password", status=401)
self.check_user(uid)
return "ok"
@route('/base_import_module/upload', type='http', auth='user', methods=['POST'])
@webservice
def upload(self, mod_file=None, **kw):
self.check_user()
imm = request.registry['ir.module.module']
if not mod_file:
raise Exception("No file sent.")
if not zipfile.is_zipfile(mod_file):
raise Exception("Not a zipfile.")
success = []
errors = dict()
with zipfile.ZipFile(mod_file, "r") as z:
for zf in z.filelist:
if zf.file_size > MAX_FILE_SIZE:
raise Exception("File '%s' exceed maximum allowed file size" % zf.filename)
with openerp.tools.osutil.tempdir() as module_dir:
z.extractall(module_dir)
dirs = [d for d in os.listdir(module_dir) if os.path.isdir(opj(module_dir, d))]
for mod_name in dirs:
try:
# assert mod_name.startswith('theme_')
path = opj(module_dir, mod_name)
imm.import_module(request.cr, request.uid, mod_name, path, context=request.context)
success.append(mod_name)
except Exception, e:
errors[mod_name] = str(e)
r = ["Successfully imported module '%s'" % mod for mod in success]
for mod, error in errors.items():
r.append("Error while importing module '%s': %r" % (mod, error))
return '\n'.join(r)

View File

@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
import ir_module

View File

@ -0,0 +1,71 @@
import logging
import os
import sys
from os.path import join as opj
import openerp
from openerp.osv import osv
from openerp.tools import convert_file
_logger = logging.getLogger(__name__)
class view(osv.osv):
_inherit = "ir.module.module"
def import_module(self, cr, uid, module, path, context=None):
known_mods = self.browse(cr, uid, self.search(cr, uid, []))
known_mods_names = dict([(m.name, m) for m in known_mods])
mod = known_mods_names.get(module)
terp = openerp.modules.load_information_from_description_file(module, mod_path=path)
values = self.get_values_from_terp(terp)
unmet_dependencies = set(terp['depends']).difference(known_mods_names.keys())
if unmet_dependencies:
raise Exception("Unmet module dependencies: %s" % ', '.join(unmet_dependencies))
if mod:
self.write(cr, uid, mod.id, values)
mode = 'update'
else:
assert terp.get('installable', True), "Module not installable"
self.create(cr, uid, dict(name=module, state='uninstalled', **values))
mode = 'init'
for kind in ['data', 'init_xml', 'update_xml']:
for filename in terp[kind]:
_logger.info("module %s: loading %s", module, filename)
noupdate = False
if filename.endswith('.csv') and kind in ('init', 'init_xml'):
noupdate = True
pathname = opj(path, filename)
idref = {}
convert_file(cr, module, filename, idref, mode=mode, noupdate=noupdate, kind=kind, pathname=pathname)
path_static = opj(path, 'static')
ir_attach = self.pool['ir.attachment']
if os.path.isdir(path_static):
for root, _, files in os.walk(path_static):
for static_file in files:
full_path = opj(root, static_file)
with open(full_path, 'r') as fp:
data = fp.read().encode('base64')
url_path = '/%s%s' % (module, full_path.split(path)[1].replace(os.path.sep, '/'))
url_path = url_path.decode(sys.getfilesystemencoding())
filename = os.path.split(url_path)[1]
values = dict(
name=filename,
datas_fname=filename,
url=url_path,
res_model='ir.ui.view',
type='binary',
datas=data,
)
att_id = ir_attach.search(cr, uid, [('url', '=', url_path), ('type', '=', 'binary'), ('res_model', '=', 'ir.ui.view')], context=context)
if att_id:
ir_attach.write(cr, uid, att_id, values, context=context)
else:
ir_attach.create(cr, uid, values, context=context)
return True

View File

@ -0,0 +1,37 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2013-Today OpenERP SA (<http://www.openerp.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
{
'name': 'Test Module',
'category': 'Website',
'summary': 'Custom',
'version': '1.0',
'description': """
Test
""",
'author': 'OpenERP SA',
'depends': ['website'],
'data': [
'test.xml',
],
'installable': True,
'application': True,
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@ -0,0 +1 @@
console.log('test_module javascript');

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<!-- TODO: make test suite -->
<record id="base.main_company" model="res.company">
<field name="name">The base company is noupdate=1</field>
</record>
<record id="main_company2" model="res.company">
<field name="name">Hagrid</field>
<field name="rml_header1">Your Company Tagline</field>
<field name="currency_id" ref="base.EUR"/>
</record>
<template id="test_page" name="Test Page" page="True">
<t t-call="website.layout">
<h1>
This page comes from an imported module!
</h1>
<p>
And this static image too !
<img src="/test_module/static/src/img/c64.png"/>
</p>
</t>
</template>
<template id="website.homepage">
<t t-call="website.layout">
<div id="wrap" class="oe_structure oe_empty">
This homepage has been overwritten by an imported module !
<p>
<a href="/page/test_module.test_page">Link to page added by imported module</a>
</p>
</div>
</t>
</template>
<template id="contactus_test" name="Contact Form" inherit_id="website.contactus">
<xpath expr="//h1" position="replace">
<h1>This contact us title has been changed by an imported module</h1>
</xpath>
</template>
</data>
</openerp>

View File

@ -1,8 +1,9 @@
# -*- coding: utf-8 -*-
import datetime
import hashlib
import logging
import re
import traceback
import werkzeug
import werkzeug.routing
@ -99,9 +100,38 @@ class ir_http(orm.AbstractModel):
path = '/' + request.lang + path
return werkzeug.utils.redirect(path)
def _serve_attachment(self):
domain = [('type', '=', 'binary'), ('url', '=', request.httprequest.path)]
attach = self.pool['ir.attachment'].search_read(request.cr, openerp.SUPERUSER_ID, domain, ['__last_update', 'datas', 'mimetype'], context=request.context)
if attach:
wdate = attach[0]['__last_update']
datas = attach[0]['datas']
response = werkzeug.wrappers.Response()
server_format = openerp.tools.misc.DEFAULT_SERVER_DATETIME_FORMAT
try:
response.last_modified = datetime.datetime.strptime(wdate, server_format + '.%f')
except ValueError:
# just in case we have a timestamp without microseconds
response.last_modified = datetime.datetime.strptime(wdate, server_format)
response.set_etag(hashlib.sha1(datas).hexdigest())
response.make_conditional(request.httprequest)
if response.status_code == 304:
return response
response.mimetype = attach[0]['mimetype']
response.set_data(datas.decode('base64'))
return response
def _handle_exception(self, exception=None, code=500):
if isinstance(exception, werkzeug.exceptions.HTTPException) and hasattr(exception, 'response') and exception.response:
return exception.response
attach = self._serve_attachment()
if attach:
return attach
if getattr(request, 'website_enabled', False) and request.website:
values = dict(
exception=exception,

View File

@ -4,6 +4,7 @@ import inspect
import itertools
import logging
import math
import mimetypes
import re
import urlparse
@ -553,7 +554,7 @@ class ir_attachment(osv.osv):
def _website_url_get(self, cr, uid, ids, name, arg, context=None):
result = {}
for attach in self.browse(cr, uid, ids, context=context):
if attach.type == 'url':
if attach.url:
result[attach.id] = attach.url
else:
result[attach.id] = urlplus('/website/image', {
@ -574,7 +575,7 @@ class ir_attachment(osv.osv):
def _compute_checksum(self, attachment_dict):
if attachment_dict.get('res_model') == 'ir.ui.view'\
and not attachment_dict.get('res_id')\
and not attachment_dict.get('res_id') and not attachment_dict.get('url')\
and attachment_dict.get('type', 'binary') == 'binary'\
and attachment_dict.get('datas'):
return hashlib.new('sha1', attachment_dict['datas']).hexdigest()
@ -600,17 +601,27 @@ class ir_attachment(osv.osv):
'website_url': fields.function(_website_url_get, string="Attachment URL", type='char'),
'datas_big': fields.function (_datas_big, type='binary', store=True,
string="Resized file content"),
'mimetype': fields.char('Mime Type', readonly=True),
}
def _add_mimetype_if_needed(self, values):
if values.get('datas_fname'):
values['mimetype'] = mimetypes.guess_type(values.get('datas_fname'))[0] or 'application/octet-stream'
def create(self, cr, uid, values, context=None):
chk = self._compute_checksum(values)
if chk:
match = self.search(cr, uid, [('datas_checksum', '=', chk)], context=context)
if match:
return match[0]
self._add_mimetype_if_needed(values)
return super(ir_attachment, self).create(
cr, uid, values, context=context)
def write(self, cr, uid, ids, values, context=None):
self._add_mimetype_if_needed(values)
return super(ir_attachment, self).write(cr, uid, ids, values, context=context)
def try_remove(self, cr, uid, ids, context=None):
""" Removes a web-based image attachment if it is used by no view
(template)

View File

@ -526,3 +526,9 @@ span[data-oe-type="monetary"] {
width: 400px;
margin: 40px auto;
}
.oe_website_overflow_ellipsis{
white-space:nowrap;
overflow:hidden;
text-overflow:ellipsis;
}

View File

@ -427,3 +427,9 @@ span[data-oe-type="monetary"]
.oe_website_login_container
width: 400px
margin: 40px auto
.oe_website_overflow_ellipsis
white-space:nowrap
overflow:hidden
text-overflow:ellipsis

View File

@ -1500,7 +1500,8 @@
args: [],
kwargs: {
fields: ['name', 'website_url'],
domain: [['res_model', '=', 'ir.ui.view']],
domain: [['res_model', '=', 'ir.ui.view'], '|',
['mimetype', '=', false], ['mimetype', '=like', 'image/%']],
order: 'id desc',
context: website.get_context(),
}

View File

@ -26,12 +26,21 @@ from openerp.tools.translate import _
from openerp.addons.website.controllers.main import Website as controllers
controllers = controllers()
import logging
_logger = logging.getLogger(__name__)
from datetime import datetime, timedelta
import time
from dateutil.relativedelta import relativedelta
from openerp import tools
import werkzeug.urls
try:
import GeoIP
except ImportError:
GeoIP = None
_logger.warn("Please install GeoIP python module to use events localisation.")
class website_event(http.Controller):
@http.route(['/event', '/event/page/<int:page>'], type='http', auth="public", website=True, multilang=True)
def events(self, page=1, **searches):
@ -203,3 +212,35 @@ class website_event(http.Controller):
}
event_id = Event.create(request.cr, request.uid, vals, context=context)
return request.redirect("/event/%s?enable_editor=1" % event_id)
def get_visitors_country(self):
GI = GeoIP.open('/usr/share/GeoIP/GeoIP.dat', 0)
return {'country_code': GI.country_code_by_addr(request.httprequest.remote_addr), 'country_name': GI.country_name_by_addr(request.httprequest.remote_addr)}
def get_formated_date(self, event):
start_date = datetime.strptime(event.date_begin, tools.DEFAULT_SERVER_DATETIME_FORMAT).date()
end_date = datetime.strptime(event.date_end, tools.DEFAULT_SERVER_DATETIME_FORMAT).date()
return ('%s %s%s') % (start_date.strftime("%b"), start_date.strftime("%e"), (end_date != start_date and ("-"+end_date.strftime("%e")) or ""))
@http.route('/event/get_country_event_list', type='http', auth='public', website=True)
def get_country_events(self ,**post):
if not GeoIP:
return ""
country_obj = request.registry['res.country']
event_obj = request.registry['event.event']
cr, uid, context,event_ids = request.cr, request.uid, request.context,[]
country_code = self.get_visitors_country()['country_code']
result = {'events':[],'country':False}
if country_code:
country_ids = country_obj.search(request.cr, request.uid, [('code', '=', country_code)], context=request.context)
event_ids = event_obj.search(request.cr, request.uid, ['|', ('address_id', '=', None),('country_id.code', '=', country_code),('date_begin','>=', time.strftime('%Y-%m-%d 00:00:00')),('state', '=', 'confirm')], order="date_begin", context=request.context)
if not event_ids:
event_ids = event_obj.search(request.cr, request.uid, [('date_begin','>=', time.strftime('%Y-%m-%d 00:00:00')),('state', '=', 'confirm')], order="date_begin", context=request.context)
for event in event_obj.browse(request.cr, request.uid, event_ids, context=request.context)[:6]:
if country_code and event.country_id.code == country_code:
result['country'] = country_obj.browse(request.cr, request.uid, country_ids[0], context=request.context)
result['events'].append({
"date": self.get_formated_date(event),
"event": event,
"url": event.website_url})
return request.website.render("website_event.country_events_list",result)

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -0,0 +1,16 @@
(function() {
"use strict";
var website = openerp.website;
website.snippet.animationRegistry.visitor = website.snippet.Animation.extend({
selector: ".oe_country_events",
start: function () {
var self = this;
$.post( "/event/get_country_event_list", function( data ) {
if(data){
$( ".country_events_list" ).replaceWith( data );
}
});
}
});
})();

View File

@ -117,6 +117,23 @@
</xpath>
</template>
<template id="event_right_country_event" inherit_option_id="website_event.index" name="Country Events">
<xpath expr="//div[@id='right_column']" position="inside">
<div class="row">
<div class="col-md-12 mb16 mt16 country_events">
<div class="country_events_list">
<div class="text-muted text-center">
<h1>
<i class="fa fa-flag fa-5x"></i>
</h1>
<h1>Events from Your Country</h1>
</div>
</div>
</div>
</div>
</xpath>
</template>
<template id="event_left_column" inherit_option_id="website_event.index" inherit_id="website_event.index" name="Filters">
<xpath expr="//div[@id='middle_column']" position="attributes">
<attribute name="class">col-md-6</attribute>
@ -126,7 +143,7 @@
<ul class="nav nav-pills nav-stacked">
<t t-foreach="dates" t-as="date">
<li t-att-class="searches.get('date') == date[0] and 'active' or ''" t-if="date[3] or (date[0] in ('old','all'))">
<a t-attf-href="/event/?{{ keep_query('country', 'type', date=date[0]) }}"><t t-esc="date[1]"/>
<a t-attf-href="/event?{{ keep_query('country', 'type', date=date[0]) }}"><t t-esc="date[1]"/>
<span t-if="date[3]" class="badge pull-right"><t t-esc="date[3]"/></span>
</a>
</li>
@ -141,7 +158,7 @@
<ul class="nav nav-pills nav-stacked mt32">
<t t-foreach="types">
<li t-if="type" t-att-class="searches.get('type') == str(type and type[0]) and 'active' or ''">
<a t-attf-href="/event/?{{ keep_query('country', 'date', type=type[0]) }}"><t t-esc="type[1]"/>
<a t-attf-href="/event?{{ keep_query('country', 'date', type=type[0]) }}"><t t-esc="type[1]"/>
<span class="badge pull-right"><t t-esc="type_count"/></span>
</a>
</li>
@ -155,7 +172,7 @@
<ul class="nav nav-pills nav-stacked mt32">
<t t-foreach="countries">
<li t-if="country_id" t-att-class="searches.get('country') == str(country_id and country_id[0]) and 'active' or ''">
<a t-attf-href="/event/?{{ keep_query('type', 'data', country=country_id[0]) }}"><t t-esc="country_id[1]"/>
<a t-attf-href="/event?{{ keep_query('type', 'data', country=country_id[0]) }}"><t t-esc="country_id[1]"/>
<span class="badge pull-right"><t t-esc="country_id_count"/></span>
</a>
</li>
@ -335,5 +352,81 @@
</div>
</t>
</template>
<template id="event_script" name="Country Events Snippet Script" inherit_id="website.layout">
<xpath expr="//head" position="inside">
<script type="text/javascript" src="/website_event/static/src/js/website_geolocation.js"></script>
</xpath>
</template>
<template id="country_events" name="Country Events" inherit_id="website.snippets">
<xpath expr="//div[@id='snippet_content']" position="inside">
<div>
<div class="oe_snippet_thumbnail">
<div style="background: white;box-shadow:none;-webkit-box-shadow:none;" class="oe_snippet_thumbnail_img" >
<i class="fa fa-flag fa-5x text-muted"></i>
</div>
<span class="oe_snippet_thumbnail_title">Local Events</span>
</div>
<div class="oe_snippet_body oe_country_events mb16 mt16">
<div class="country_events_list">
<div class="text-muted text-left">
<div>
<img style="max-height:10em;" class="img-rounded img-responsive" src="/website_event/static/src/img/world_map.jpg"></img>
</div>
<div>Events in visitor's country</div>
</div>
</div>
</div>
</div>
</xpath>
<xpath expr="//div[@id='snippet_options']" position="inside">
<div data-snippet-option-id='content'
data-selector=".oe_country_events"
data-selector-siblings="p, h1, h2, h3, blockquote, .well, .panel"
data-selector-children=".content"
>
</div>
</xpath>
</template>
<template id="country_events_list" name="Country">
<t t-ignore="true">
<t t-if="events">
<div class="country_events_list">
<div>
<t t-if="country">
<img style="max-height:10em;" class="img-rounded img-responsive" t-att-src="'/website/image?model=res.country&amp;field=image&amp;id='+str(country.id)"></img>
<h4><b>Events in <span t-esc="country.name"></span></b></h4>
</t>
<t t-if="not country">
<img style="max-height:10em;" class="img-rounded img-responsive" src="/website_event/static/src/img/world_map.jpg"></img>
<h4><b>Upcoming Events</b></h4>
</t>
<div t-foreach="events[:5]" t-as="event_dict" class="oe_website_overflow_ellipsis mb8">
<t t-if="not event_dict['event'].website_published">
<span class="label label-danger"><i class="fa fa-ban"></i></span>
</t>
<t t-if="event_dict['event'].address_id">
<i class="fa fa-map-marker"></i>
</t>
<t t-if="not event_dict['event'].address_id">
<i class="fa fa-cloud"></i>
</t>
<b><span t-esc="event_dict['date']"/>: <span><a t-att-href="event_dict['url']"><t t-esc="event_dict['event'].name"/></a></span></b>
</div>
<t t-if="len(events) > 5">
<div t-if="country">
<b><a t-att-href="'/event?country='+str(country.id)">See all events from <span t-esc="country.name"></span></a></b>
</div>
<div t-if="not country">
<b><a href="/event"> See all upcoming events </a></b>
</div>
</t>
</div>
</div>
</t>
</t>
</template>
</data>
</openerp>