[ADD]Missed out files in previous commit
This commit is contained in:
parent
4fc1dab3fe
commit
6bef1c5409
|
@ -12,5 +12,5 @@ Tryton module to support Nereid
|
|||
from .routing import *
|
||||
#from auth import User, Address
|
||||
from .template import Template
|
||||
#from static_bank import NereidStaticFolder, NereidStaticFile
|
||||
from .static_file import NereidStaticFolder, NereidStaticFile
|
||||
from .party import Address
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
'xml': [
|
||||
# 'defaults.xml',
|
||||
'configuration.xml',
|
||||
'static_file.xml'
|
||||
],
|
||||
'translation': [
|
||||
],
|
||||
|
|
|
@ -31,14 +31,18 @@
|
|||
<data>
|
||||
<xpath expr="/form/field[@name="subdivision"]"
|
||||
position="after">
|
||||
<newline/>
|
||||
<label name="email"/>
|
||||
<field name="email"/>
|
||||
<label name="phone"/>
|
||||
<field name="phone"/>
|
||||
<label name="username"/>
|
||||
<field name="username"/>
|
||||
<label name="password"/>
|
||||
<field name="password"/>
|
||||
<newline/>
|
||||
<button string="Create Web Account"
|
||||
name="create_web_account" type="object"
|
||||
confirm="Account has been created" colspan="3"/>
|
||||
<button string="Reset Account"
|
||||
name="reset_web_account" type="object"
|
||||
confirm="Account has been reset" colspan="3"/>
|
||||
</xpath>
|
||||
</data>
|
||||
]]>
|
||||
|
@ -64,6 +68,11 @@
|
|||
<field name="active" />
|
||||
<label name="company"/>
|
||||
<field name="company"/>
|
||||
<notebook colspan="4">
|
||||
<page id="Countries" string="Countries">
|
||||
<field name="countries"/>
|
||||
</page>
|
||||
</notebook>
|
||||
</form>
|
||||
]]>
|
||||
</field>
|
||||
|
|
94
party.py
94
party.py
|
@ -10,10 +10,17 @@
|
|||
'''
|
||||
import random
|
||||
import string
|
||||
try:
|
||||
import hashlib
|
||||
except ImportError:
|
||||
hashlib = None
|
||||
import sha
|
||||
|
||||
from trytond.model import ModelView, ModelSQL, fields
|
||||
from trytond.pyson import Eval
|
||||
|
||||
|
||||
# pylint: disable-msg=E1101
|
||||
class Address(ModelSQL, ModelView):
|
||||
"""An address is considered as the equivalent of a user
|
||||
in a conventional Web application. Hence, the username and
|
||||
|
@ -23,13 +30,14 @@ class Address(ModelSQL, ModelView):
|
|||
|
||||
#: The email to which all application related emails like
|
||||
#: registration, password reet etc is managed
|
||||
email = fields.Many2One('party.contact_mechanism', 'E-Mail')
|
||||
email = fields.Many2One('party.contact_mechanism', 'E-Mail',
|
||||
domain=[('party', '=', Eval('party')), ('type', '=', 'email')],
|
||||
depends=['party'])
|
||||
|
||||
#: Similar to email
|
||||
phone = fields.Many2One('party.contact_mechanism', 'Phone')
|
||||
|
||||
#: Username for login
|
||||
username = fields.Char('Username')
|
||||
phone = fields.Many2One('party.contact_mechanism', 'Phone',
|
||||
domain=[('party', '=', Eval('party')), ('type', '=', 'phone')],
|
||||
depends=['party'])
|
||||
|
||||
#: The password is the user password + the salt, which is
|
||||
#: then hashed together
|
||||
|
@ -37,10 +45,82 @@ class Address(ModelSQL, ModelView):
|
|||
|
||||
#: The salt which was used to make the hash is separately
|
||||
#: stored. Needed for
|
||||
salt = fields.Char('Salt')
|
||||
salt = fields.Char('Salt', size=8)
|
||||
|
||||
def __init__(self):
|
||||
super(Address, self).__init__()
|
||||
self._sql_constraints += [
|
||||
('unique_email', 'UNIQUE(email)',
|
||||
'email must be unique.'),
|
||||
]
|
||||
self._error_messages.update({
|
||||
'no_email': 'The user does not have an email assigned'
|
||||
})
|
||||
self._rpc.update({
|
||||
'create_web_account': True,
|
||||
'reset_web_account': True
|
||||
})
|
||||
|
||||
def create_web_account(self, ids):
|
||||
"""Create a new web account for given address
|
||||
"""
|
||||
for address in self.browse(ids):
|
||||
if not address.email:
|
||||
self.raise_user_error('no_email')
|
||||
password = ''.join(
|
||||
random.sample(string.letters + string.digits, 16))
|
||||
self.write(address.id, {'password': password})
|
||||
return True
|
||||
|
||||
def reset_web_account(self, ids):
|
||||
"""Reset the password for the user"""
|
||||
for address in self.browse(ids):
|
||||
if not address.email:
|
||||
self.raise_user_error('no_email')
|
||||
password = ''.join(
|
||||
random.sample(string.letters + string.digits, 16))
|
||||
self.write(address.id, {'password': password})
|
||||
return True
|
||||
|
||||
def authenticate(self, email, password):
|
||||
"""Assert credentials and if correct return the
|
||||
browse record of the user
|
||||
|
||||
:param email: email of the user
|
||||
:param password: password of the user
|
||||
:return: Browse Record or None
|
||||
"""
|
||||
contact_mech_obj = self.pool.get('party.contact_mechanism')
|
||||
contact = contact_mech_obj.search([
|
||||
('value', '=', email),
|
||||
('type', '=', 'email')])
|
||||
if not contact:
|
||||
return None
|
||||
|
||||
ids = self.search([
|
||||
('email', '=', contact[0])
|
||||
])
|
||||
if not ids or len(ids) > 1:
|
||||
return None
|
||||
|
||||
address = self.browse(ids[0])
|
||||
password += address.salt or ''
|
||||
|
||||
if isinstance(password, unicode):
|
||||
password = password.encode('utf-8')
|
||||
|
||||
if hashlib:
|
||||
password_sha = hashlib.sha1(password).hexdigest()
|
||||
else:
|
||||
password_sha = sha.new(password).hexdigest()
|
||||
|
||||
if password_sha == address.password:
|
||||
return address
|
||||
|
||||
return None
|
||||
|
||||
def _convert_values(self, values):
|
||||
if 'password' in values:
|
||||
if 'password' in values and values['password']:
|
||||
values['salt'] = ''.join(random.sample(
|
||||
string.ascii_letters + string.digits, 8))
|
||||
values['password'] += values['salt']
|
||||
|
|
209
routing.py
209
routing.py
|
@ -9,10 +9,15 @@
|
|||
:license: GPLv3, see LICENSE for more details
|
||||
"""
|
||||
from ast import literal_eval
|
||||
from nereid import request
|
||||
|
||||
from werkzeug import abort, redirect
|
||||
from nereid import jsonify, flash, render_template
|
||||
from nereid.globals import session, request
|
||||
from trytond.model import ModelView, ModelSQL, fields
|
||||
from wtforms import Form, TextField, PasswordField, validators
|
||||
|
||||
|
||||
# pylint: disable-msg=E1101
|
||||
class URLMap(ModelSQL, ModelView):
|
||||
"""
|
||||
URL Map
|
||||
|
@ -81,6 +86,12 @@ class URLMap(ModelSQL, ModelView):
|
|||
URLMap()
|
||||
|
||||
|
||||
class LoginForm(Form):
|
||||
"Default Login Form"
|
||||
email = TextField('e-mail', [validators.Required(), validators.Email()])
|
||||
password = PasswordField('Password', [validators.Required()])
|
||||
|
||||
|
||||
class WebSite(ModelSQL, ModelView):
|
||||
"""
|
||||
Web Site
|
||||
|
@ -116,7 +127,13 @@ class WebSite(ModelSQL, ModelView):
|
|||
#: records like sale order which require a company to be present
|
||||
company = fields.Many2One('company.company', 'Company', required=True)
|
||||
|
||||
active= fields.Boolean('Active')
|
||||
active = fields.Boolean('Active')
|
||||
|
||||
#: The list of countries this website operates in. Used for generating
|
||||
#: Countries list in the registration form etc.
|
||||
countries = fields.Many2Many(
|
||||
'nereid.website-country.country', 'website', 'country',
|
||||
'Countries Available')
|
||||
|
||||
def default_active(self):
|
||||
return True
|
||||
|
@ -128,6 +145,30 @@ class WebSite(ModelSQL, ModelView):
|
|||
'Another site with the same name already exists!')
|
||||
]
|
||||
|
||||
def country_list(self):
|
||||
"""
|
||||
Return the list of countries in JSON
|
||||
"""
|
||||
return jsonify([
|
||||
{'key': c.id, 'value': c.name} \
|
||||
for c in request.nereid_website.countries
|
||||
])
|
||||
|
||||
def subdivision_list(self):
|
||||
"""
|
||||
Return the list of states for given country
|
||||
"""
|
||||
country = int(request.args.get('country', 0))
|
||||
if country not in [c.id for c in request.nereid_website.countries]:
|
||||
abort(404)
|
||||
|
||||
subdivision_obj = self.pool.get('country.subdivision')
|
||||
ids = subdivision_obj.search([('country', '=', country)])
|
||||
subdivisions = subdivision_obj.browse(ids)
|
||||
return jsonify(
|
||||
result = [{'key': s.id, 'value': s.name} for s in subdivisions]
|
||||
)
|
||||
|
||||
def get_urls(self, name):
|
||||
"""
|
||||
Return complete list of URLs
|
||||
|
@ -147,6 +188,155 @@ class WebSite(ModelSQL, ModelView):
|
|||
return u'Request: %s\nArguments: %s\nEnviron: %s\n' \
|
||||
% (request, arguments, request.environ)
|
||||
|
||||
def home(self):
|
||||
"""
|
||||
A sample home method
|
||||
"""
|
||||
return u'''Welcome to Nereid
|
||||
This is the default home page and needs replacing. To build
|
||||
your own home method, inherit the model nereid.website and implement
|
||||
the `home` method to replace this function.
|
||||
'''
|
||||
|
||||
def login(self):
|
||||
"""
|
||||
Simple login based on the email and password
|
||||
|
||||
Required post data see :class:LoginForm
|
||||
"""
|
||||
login_form = LoginForm(request.form)
|
||||
|
||||
if request.method == 'POST' and login_form.validate():
|
||||
address_obj = self.pool.get('party.address')
|
||||
result = address_obj.authenticate(
|
||||
login_form.email.data, login_form.password.data)
|
||||
if result is None:
|
||||
flash("Invalid login credentials")
|
||||
else:
|
||||
flash("You are now logged in. Welcome %s" % result.name)
|
||||
session['user'] = result.id
|
||||
return redirect(request.args.get('next', '/'))
|
||||
return render_template('login.jinja', login_form=login_form)
|
||||
|
||||
def logout(self):
|
||||
"Log the user out"
|
||||
session.pop('user', None)
|
||||
flash('You have been logged out successfully. Thanks for visiting us')
|
||||
return redirect(request.args.get('next', '/'))
|
||||
|
||||
def registration(self):
|
||||
register_form = RegistrationForm(request.form)
|
||||
register_form.country.choices = [
|
||||
(c.id, c.name) for c in request.nereid_website.countries
|
||||
]
|
||||
if request.method == 'POST' and register_form.validate():
|
||||
address_obj = self.pool.get('party.address')
|
||||
contact_mech_obj = self.pool.get('party.contact_mechanism')
|
||||
party_obj = self.pool.get('party.party')
|
||||
opportunity_obj = self.pool.get('sale.opportunity')
|
||||
|
||||
registration_data = register_form.data
|
||||
|
||||
# First search if an address with the email already exists
|
||||
existing = contact_mech_obj.search([
|
||||
('value', '=', registration_data['email']),
|
||||
('type', '=', 'email')])
|
||||
if existing:
|
||||
flash('A registration already exists with this email. '
|
||||
'Please contact customer care')
|
||||
else:
|
||||
# Create Party
|
||||
party_id = party_obj.create({
|
||||
'name': registration_data['company'],
|
||||
'addresses': [
|
||||
('create', {
|
||||
'name': registration_data['name'],
|
||||
'street': registration_data['street'],
|
||||
'streetbis': registration_data['streetbis'],
|
||||
'zip': registration_data['zip'],
|
||||
'city': registration_data['city'],
|
||||
'country': registration_data['country'],
|
||||
'subdivision': registration_data['subdivision'],
|
||||
})],
|
||||
})
|
||||
party = party_obj.browse(party_id)
|
||||
|
||||
# Create email as contact mech and assign as email
|
||||
contact_mech_id = contact_mech_obj.create({
|
||||
'type': 'email',
|
||||
'party': party.id,
|
||||
'email': registration_data['email'],
|
||||
})
|
||||
address_obj.write(party.addresses[0].id,
|
||||
{'email': contact_mech_id})
|
||||
|
||||
# Finally create opportunity
|
||||
opportunity = opportunity_obj.create({
|
||||
'party': party.id,
|
||||
'address': party.addresses[0].id,
|
||||
'company': request.nereid_website.company.id,
|
||||
'description': 'New Registration',
|
||||
'comment': registration_data['comments'],
|
||||
'employee': request.nereid_website.default_employee.id
|
||||
})
|
||||
|
||||
flash('''Your registration has been completed successfully
|
||||
Our customer care will soon be in touch with you.
|
||||
Your registration number is: %s''' % opportunity)
|
||||
return redirect(request.args.get('next', '/'))
|
||||
return render_template('registration.jinja', form=register_form)
|
||||
|
||||
def registration(self):
|
||||
register_form = RegistrationForm(request.form)
|
||||
register_form.country.choices = [
|
||||
(c.id, c.name) for c in request.nereid_website.countries
|
||||
]
|
||||
|
||||
if request.method == 'POST' and register_form.validate():
|
||||
address_obj = self.pool.get('party.address')
|
||||
contact_mech_obj = self.pool.get('party.contact_mechanism')
|
||||
party_obj = self.pool.get('party.party')
|
||||
|
||||
registration_data = register_form.data
|
||||
|
||||
# First search if an address with the email already exists
|
||||
existing = contact_mech_obj.search([
|
||||
('value', '=', registration_data['email']),
|
||||
('type', '=', 'email')])
|
||||
if existing:
|
||||
flash('A registration already exists with this email. '
|
||||
'Please contact customer care')
|
||||
else:
|
||||
# Create Party
|
||||
party_id = party_obj.create({
|
||||
'name': registration_data['company'],
|
||||
'addresses': [
|
||||
('create', {
|
||||
'name': registration_data['name'],
|
||||
'street': registration_data['street'],
|
||||
'streetbis': registration_data['streetbis'],
|
||||
'zip': registration_data['zip'],
|
||||
'city': registration_data['city'],
|
||||
'country': registration_data['country'],
|
||||
'subdivision': registration_data['subdivision'],
|
||||
})],
|
||||
})
|
||||
party = party_obj.browse(party_id)
|
||||
|
||||
# Create email as contact mech and assign as email
|
||||
contact_mech_id = contact_mech_obj.create({
|
||||
'type': 'email',
|
||||
'party': party.id,
|
||||
'email': registration_data['email'],
|
||||
})
|
||||
address_obj.write(party.addresses[0].id,
|
||||
{'email': contact_mech_id})
|
||||
|
||||
flash('''Your registration has been completed successfully
|
||||
Our customer care will soon be in touch with you.''')
|
||||
return redirect(request.args.get('next', '/'))
|
||||
return render_template('registration.jinja', form=register_form)
|
||||
|
||||
WebSite()
|
||||
|
||||
|
||||
|
@ -199,7 +389,7 @@ class URLRule(ModelSQL, ModelView):
|
|||
|
||||
rule = fields.Char('Rule', required=True, select=True,)
|
||||
endpoint = fields.Char('Endpoint', required=True, select=True,)
|
||||
active= fields.Boolean('Active')
|
||||
active = fields.Boolean('Active')
|
||||
defaults = fields.One2Many('nereid.url_rule_defaults', 'rule', 'Defaults')
|
||||
methods = fields.Selection(
|
||||
[
|
||||
|
@ -210,7 +400,7 @@ class URLRule(ModelSQL, ModelView):
|
|||
'Methods', required=True)
|
||||
only_for_genaration = fields.Boolean('Only for Generation')
|
||||
redirect_to = fields.Char('Redirect To')
|
||||
sequence= fields.Integer('Sequence', required=True,)
|
||||
sequence = fields.Integer('Sequence', required=True,)
|
||||
url_map = fields.Many2One('nereid.urlmap', 'URL Map')
|
||||
|
||||
def __init__(self):
|
||||
|
@ -261,3 +451,14 @@ class URLRuleDefaults(ModelSQL, ModelView):
|
|||
select=True)
|
||||
|
||||
URLRuleDefaults()
|
||||
|
||||
|
||||
class WebsiteCountry(ModelSQL):
|
||||
"Website Country Relations"
|
||||
_name = 'nereid.website-country.country'
|
||||
_description = __doc__
|
||||
|
||||
website = fields.Many2One('nereid.website', 'Website')
|
||||
country = fields.Many2One('country.country', 'Country')
|
||||
|
||||
WebsiteCountry()
|
||||
|
|
|
@ -0,0 +1,273 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: UTF-8 -*-
|
||||
'''
|
||||
nereid.static_file
|
||||
|
||||
Static file
|
||||
|
||||
:copyright: (c) 2010 by Sharoon Thomas.
|
||||
:license: BSD, see LICENSE for more details
|
||||
'''
|
||||
import os
|
||||
import base64
|
||||
from collections import defaultdict
|
||||
|
||||
from nereid.helpers import slugify, send_file
|
||||
from werkzeug import abort
|
||||
|
||||
from trytond.model import ModelSQL, ModelView, fields
|
||||
from trytond.config import CONFIG
|
||||
from trytond.transaction import Transaction
|
||||
|
||||
|
||||
def get_nereid_path():
|
||||
"Returns base path for nereid"
|
||||
cursor = Transaction().cursor
|
||||
return os.path.join(CONFIG['data_path'], cursor.database_name)
|
||||
|
||||
|
||||
def make_folder_path(folder_name):
|
||||
"Returns the folder path for given folder"
|
||||
return os.path.join(get_nereid_path(), folder_name)
|
||||
|
||||
|
||||
def make_file_path(folder_name, file_name):
|
||||
"Returns the file path for the given folder, file"
|
||||
return os.path.join(make_folder_path(folder_name), file_name)
|
||||
|
||||
|
||||
def make_file(file_name, file_binary, folder):
|
||||
"""
|
||||
Writes file to the FS
|
||||
|
||||
:param file_name: Name of the file
|
||||
:param file_binary: Binary content to save (Base64 encoded)
|
||||
:param folder: folder name
|
||||
"""
|
||||
file_binary = base64.decodestring(file_binary)
|
||||
file_path = make_file_path(folder, file_name)
|
||||
with open(file_path, 'wb') as file_writer:
|
||||
file_writer.write(file_binary)
|
||||
return file_path
|
||||
|
||||
|
||||
# pylint: disable-msg=E1101
|
||||
|
||||
class NereidStaticFolder(ModelSQL, ModelView):
|
||||
"Static folder for Nereid"
|
||||
_name = "nereid.static.folder"
|
||||
_description = __doc__
|
||||
|
||||
name = fields.Char('Description', required=True, select=1)
|
||||
folder_name = fields.Char('Folder Name', required=True, select=1,
|
||||
on_change_with=['name', 'folder_name'])
|
||||
comments = fields.Text('Comments')
|
||||
files = fields.One2Many('nereid.static.file', 'folder', 'Files')
|
||||
folder_path = fields.Function(fields.Char('Folder Path'), 'get_path')
|
||||
|
||||
def __init__(self):
|
||||
super(NereidStaticFolder, self).__init__()
|
||||
self._constraints += [
|
||||
('check_folder_name', 'invalid_folder_name'),
|
||||
]
|
||||
self._sql_constraints += [
|
||||
('unique_folder', 'UNIQUE(folder_name)',
|
||||
'Folder name needs to be unique')
|
||||
]
|
||||
self._error_messages.update({
|
||||
'invalid_folder_name': """Invalid folder name:
|
||||
(1) '.' in folder name (OR)
|
||||
(2) folder name begins with '/'""",
|
||||
'folder_cannot_change': "Folder name cannot be changed"
|
||||
})
|
||||
|
||||
|
||||
def get_path(self, ids, name):
|
||||
"""Return the path of the folder
|
||||
"""
|
||||
result = { }
|
||||
for folder in self.browse(ids):
|
||||
result[folder.id] = make_folder_path(folder.folder_name)
|
||||
return result
|
||||
|
||||
def on_change_with_folder_name(self, vals):
|
||||
"""
|
||||
Fills the name field with a slugified name
|
||||
"""
|
||||
if vals.get('name'):
|
||||
if not vals.get('folder_name'):
|
||||
vals['folder_name'] = slugify(vals['name'])
|
||||
return vals['folder_name']
|
||||
|
||||
def check_folder_name(self, ids):
|
||||
'''
|
||||
Check the validity of folder name
|
||||
Allowing the use of / or . will be risky as that could
|
||||
eventually lead to previlege escalation
|
||||
|
||||
:param ids: ID of current record.
|
||||
'''
|
||||
folder = self.browse(ids[0])
|
||||
if ('.' in folder.folder_name) or (folder.folder_name.startswith('/')):
|
||||
return False
|
||||
return True
|
||||
|
||||
def create(self, vals):
|
||||
"""
|
||||
Check if the folder exists.
|
||||
If not, create a new one in data path of tryton.
|
||||
|
||||
:param vals: values of the current record
|
||||
"""
|
||||
folder_path = make_folder_path(vals.get('folder_name'))
|
||||
|
||||
# Create the nereid folder if it doesnt exist
|
||||
if not os.path.isdir(get_nereid_path()):
|
||||
os.mkdir(get_nereid_path())
|
||||
|
||||
# Create the folder if it doesnt exist
|
||||
if not os.path.isdir(folder_path):
|
||||
os.mkdir(folder_path)
|
||||
|
||||
return super(NereidStaticFolder, self).create(vals)
|
||||
|
||||
def write(self, ids, vals):
|
||||
"""
|
||||
Check if the folder_name has been modified.
|
||||
If yes, raise an error.
|
||||
|
||||
:param vals: values of the current record
|
||||
"""
|
||||
if vals.get('folder_name'):
|
||||
self.raise_user_error('folder_cannot_change')
|
||||
return super(NereidStaticFolder, self).write(ids, vals)
|
||||
|
||||
def scan_files_from_fs(self, folder_ids):
|
||||
"""
|
||||
Scans File system for images and syncs them
|
||||
|
||||
:param folder_ids: ID of the System Folder
|
||||
"""
|
||||
file_object = self.pool.get('nereid.static.file')
|
||||
|
||||
for folder in self.browse(folder_ids):
|
||||
existing_filenames = [f.name for f in folder.files]
|
||||
|
||||
folder_path = make_folder_path(folder.folder_name)
|
||||
for content in os.listdir(folder_path):
|
||||
full_path = os.path.join(folder_path, content)
|
||||
|
||||
if (os.path.isfile(full_path)) and \
|
||||
(content not in existing_filenames):
|
||||
file_object.create({'name': content, 'folder': folder.id})
|
||||
return True
|
||||
|
||||
NereidStaticFolder()
|
||||
|
||||
|
||||
class NereidStaticFile(ModelSQL, ModelView):
|
||||
"Static files for Nereid"
|
||||
_name = "nereid.static.file"
|
||||
_description = __doc__
|
||||
|
||||
name = fields.Char('File Name', select=True, required=True)
|
||||
file_ = fields.Function(fields.Binary('File'),
|
||||
'get_field_binary', 'set_content')
|
||||
folder = fields.Many2One('nereid.static.folder', 'Folder', required=True)
|
||||
file_path = fields.Function(fields.Char('File Path'), 'get_fields',)
|
||||
relative_path = fields.Function(fields.Char('Relative Path'), 'get_fields')
|
||||
|
||||
def __init__(self):
|
||||
super(NereidStaticFile, self).__init__()
|
||||
self._constraints += [
|
||||
('check_file_name', 'invalid_file_name'),
|
||||
]
|
||||
self._sql_constraints += [
|
||||
('name_folder_uniq', 'UNIQUE(name, folder)',
|
||||
'The Name of the Static File must be unique in a folder.!'),
|
||||
]
|
||||
self._error_messages.update({
|
||||
'invalid_file_name': """Invalid file name:
|
||||
(1) '..' in file name (OR)
|
||||
(2) file name contains '/'""",
|
||||
})
|
||||
|
||||
def set_content(self, ids, name, value):
|
||||
"""
|
||||
Creates the file for the function field
|
||||
"""
|
||||
for file_ in self.browse(ids):
|
||||
make_file(file_.name, value, file_.folder.folder_name)
|
||||
|
||||
def get_fields(self, ids, names):
|
||||
'''
|
||||
Function to compute function fields for sale ids
|
||||
|
||||
:param ids: the ids of the sales
|
||||
:param names: the list of field name to compute
|
||||
:return: a dictionary with all field names as key and
|
||||
a dictionary as value with id as key
|
||||
'''
|
||||
res = defaultdict(dict)
|
||||
for name in names:
|
||||
res[name] = { }
|
||||
|
||||
for file_ in self.browse(ids):
|
||||
file_path = os.path.join(
|
||||
make_file_path(file_.folder.folder_name, file_.name))
|
||||
|
||||
if 'file_path' in names:
|
||||
res['file_path'][file_.id] = file_path
|
||||
|
||||
|
||||
if 'relative_path' in names:
|
||||
res['relative_path'][file_.id] = '/'.join([
|
||||
file_.folder.folder_name,
|
||||
file_.name])
|
||||
|
||||
return res
|
||||
|
||||
def get_field_binary(self, ids, name):
|
||||
"""
|
||||
This could be part of the above function, but this is an
|
||||
expensive process which must not affect the rest of the processes
|
||||
"""
|
||||
result = {}
|
||||
for file_ in self.browse(ids):
|
||||
file_path = os.path.join(
|
||||
make_file_path(file_.folder.folder_name, file_.name))
|
||||
with open(file_path, 'rb') as file_handler:
|
||||
result[file_.id] = base64.encodestring(
|
||||
file_handler.read()
|
||||
)
|
||||
return result
|
||||
|
||||
def check_file_name(self, ids):
|
||||
'''
|
||||
Check the validity of folder name
|
||||
Allowing the use of / or . will be risky as that could
|
||||
eventually lead to previlege escalation
|
||||
|
||||
:param ids: ID of current record.
|
||||
'''
|
||||
file_ = self.browse(ids[0])
|
||||
if ('..' in file_.name) or ('/' in file_.name):
|
||||
return False
|
||||
return True
|
||||
|
||||
def send_static_file(self, folder, name):
|
||||
'''
|
||||
Send the static file
|
||||
'''
|
||||
#TODO: Separate this search and find into separate cached method
|
||||
|
||||
ids = self.search([
|
||||
('folder.folder_name', '=', folder),
|
||||
('name', '=', name)
|
||||
])
|
||||
if not ids:
|
||||
abort(404)
|
||||
file_ = self.browse(ids[0])
|
||||
return send_file(file_.file_path)
|
||||
|
||||
NereidStaticFile()
|
|
@ -0,0 +1,149 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
This file is part of Tryton & Nereid. The COPYRIGHT file at the
|
||||
top level of this repository contains the full copyright notices
|
||||
and license terms.
|
||||
-->
|
||||
<tryton>
|
||||
<data>
|
||||
<!-- Static Bank -->
|
||||
<record id="nereid_static_folder_form" model="ir.ui.view">
|
||||
<field name="model">nereid.static.folder</field>
|
||||
<field name="type">form</field>
|
||||
<field name="arch" type="xml">
|
||||
<![CDATA[
|
||||
<form string="Static Folder">
|
||||
<label name="name" />
|
||||
<field name="name" />
|
||||
<label name="folder_name" />
|
||||
<field name="folder_name" />
|
||||
<newline />
|
||||
<field name="files" colspan="4" >
|
||||
<form string="File">
|
||||
<label name="name" />
|
||||
<field name="name" />
|
||||
<label name="file_" />
|
||||
<field name="file_" />
|
||||
<label name="folder" />
|
||||
<field name="folder" />
|
||||
<label name="file_path" />
|
||||
<field name="file_path" />
|
||||
</form>
|
||||
<tree string="Files">
|
||||
<field name="name" />
|
||||
<field name="folder" />
|
||||
<field name="file_path" />
|
||||
</tree>
|
||||
</field>
|
||||
<button name="scan_files_from_fs"
|
||||
string="Create files from folder"
|
||||
type="object"
|
||||
colspan="2" />
|
||||
<separator colspan="4" string="Comments"
|
||||
id="sepr_comments"/>
|
||||
<field name="comments" colspan="4" />
|
||||
</form>
|
||||
]]>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="nereid_static_folder_tree" model="ir.ui.view">
|
||||
<field name="model">nereid.static.folder</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="arch" type="xml">
|
||||
<![CDATA[
|
||||
<tree>
|
||||
<field name="name" />
|
||||
<field name="folder_name" />
|
||||
</tree>
|
||||
]]>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.act_window" id="action_nereid_static_folder_view">
|
||||
<field name="name">Nereid Static Folders</field>
|
||||
<field name="res_model">nereid.static.folder</field>
|
||||
<field name="view_type">form</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_nereid_static_folder_view1">
|
||||
<field name="sequence" eval="10" />
|
||||
<field name="view" ref="nereid_static_folder_tree" />
|
||||
<field name="act_window" ref="action_nereid_static_folder_view" />
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_nereid_static_folder_view2">
|
||||
<field name="sequence" eval="20" />
|
||||
<field name="view" ref="nereid_static_folder_form" />
|
||||
<field name="act_window" ref="action_nereid_static_folder_view" />
|
||||
</record>
|
||||
|
||||
<menuitem id="menu_nereid_static"
|
||||
parent="menu_nereid"
|
||||
name="Static"
|
||||
groups="nereid.group_nereid_admin" />
|
||||
|
||||
<menuitem name="Static Folders" sequence="20"
|
||||
id="menu_nereid_config_static_folder"
|
||||
action="action_nereid_static_folder_view"
|
||||
parent="menu_nereid_static"
|
||||
groups="nereid.group_nereid_admin" />
|
||||
|
||||
<record id="nereid_static_file_form" model="ir.ui.view">
|
||||
<field name="model">nereid.static.file</field>
|
||||
<field name="type">form</field>
|
||||
<field name="arch" type="xml">
|
||||
<![CDATA[
|
||||
<form>
|
||||
<label name="file_" />
|
||||
<field name="file_" />
|
||||
<label name="name" />
|
||||
<field name="name" />
|
||||
<label name="folder" />
|
||||
<field name="folder" />
|
||||
<separator colspan="4" id="paths" string="Paths"/>
|
||||
<label name="relative_path" />
|
||||
<field name="relative_path" />
|
||||
<label name="file_path" />
|
||||
<field name="file_path" />
|
||||
<separator string="Preview"
|
||||
colspan="4" id="sepr_preview"/>
|
||||
<field name="file_" widget="image" colspan="4"/>
|
||||
</form>
|
||||
]]>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="nereid_static_file_tree" model="ir.ui.view">
|
||||
<field name="model">nereid.static.file</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="arch" type="xml">
|
||||
<![CDATA[
|
||||
<tree>
|
||||
<field name="name" />
|
||||
<field name="folder" />
|
||||
</tree>
|
||||
]]>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.act_window" id="action_nereid_static_file_view">
|
||||
<field name="name">Nereid Static Files</field>
|
||||
<field name="res_model">nereid.static.file</field>
|
||||
<field name="view_type">form</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_nereid_static_file_view1">
|
||||
<field name="sequence" eval="10" />
|
||||
<field name="view" ref="nereid_static_file_tree" />
|
||||
<field name="act_window" ref="action_nereid_static_file_view" />
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_nereid_static_file_view2">
|
||||
<field name="sequence" eval="20" />
|
||||
<field name="view" ref="nereid_static_file_form" />
|
||||
<field name="act_window" ref="action_nereid_static_file_view" />
|
||||
</record>
|
||||
|
||||
<menuitem id="menu_nereid_config_static_file"
|
||||
parent="menu_nereid_static"
|
||||
name="Static Files"
|
||||
action="action_nereid_static_file_view" />
|
||||
</data>
|
||||
</tryton>
|
33
template.py
33
template.py
|
@ -9,6 +9,8 @@
|
|||
:license: GPLv3, see LICENSE for more details
|
||||
"""
|
||||
from trytond.model import ModelView, ModelSQL, fields
|
||||
from trytond.transaction import Transaction
|
||||
|
||||
|
||||
class Template(ModelSQL, ModelView):
|
||||
"""
|
||||
|
@ -52,9 +54,9 @@ class Template(ModelSQL, ModelView):
|
|||
|
||||
If not found it returns None
|
||||
"""
|
||||
lang_obj = self.pool.get('res.lang')
|
||||
lang_obj = self.pool.get('ir.lang')
|
||||
lang_id, = lang_obj.search(
|
||||
[('code', '=', context.get('language', 'en_US'))]
|
||||
[('code', '=', Transaction().context.get('language', 'en_US'))]
|
||||
)
|
||||
template_ids = self.search(
|
||||
[('name', '=', name), ('language', '=', lang_id)]
|
||||
|
@ -66,3 +68,30 @@ class Template(ModelSQL, ModelView):
|
|||
|
||||
Template()
|
||||
|
||||
|
||||
class ContextProcessors(ModelSQL, ModelView):
|
||||
"Temlate Context Processor Registry"
|
||||
_name = 'nereid.template.context_processor'
|
||||
_description = __doc__
|
||||
_rec_name = 'function'
|
||||
|
||||
method = fields.Char('Method', required=True,
|
||||
help="Context processor method in <model>.<method>")
|
||||
model = fields.Char('Model',
|
||||
help="This will restrict the loading when URLs with"
|
||||
" the model are called")
|
||||
|
||||
def get_processors(self):
|
||||
"""Return the list of processors. Separate function
|
||||
since its important to have caching on this
|
||||
"""
|
||||
result = { }
|
||||
ids = self.search([])
|
||||
for ctx_proc in self.browse(ids):
|
||||
model, method = ctx_proc.method.rsplit('.', 1)
|
||||
ctx_proc_as_func = getattr(self.pool.get(model), method)
|
||||
result.setdefault(ctx_proc.model or None, []).append(
|
||||
ctx_proc_as_func)
|
||||
return result
|
||||
|
||||
ContextProcessors()
|
||||
|
|
Loading…
Reference in New Issue