parent
c39e302253
commit
33b5d4aba8
16
__init__.py
16
__init__.py
|
@ -2,19 +2,21 @@
|
|||
# this repository contains the full copyright notices and license terms.
|
||||
from trytond.pool import Pool
|
||||
|
||||
from .party import *
|
||||
from .routing import *
|
||||
from .static_file import *
|
||||
from .currency import *
|
||||
from .template import *
|
||||
from .party import Address, Party, ContactMechanism, NereidUser, Permission, \
|
||||
UserPermission
|
||||
from .routing import URLMap, WebSite, URLRule, URLRuleDefaults, \
|
||||
WebsiteCountry, WebsiteCurrency
|
||||
from .static_file import NereidStaticFolder, NereidStaticFile
|
||||
from .currency import Currency, Language
|
||||
from .template import ContextProcessors
|
||||
|
||||
|
||||
def register():
|
||||
Pool.register(
|
||||
Address,
|
||||
Party,
|
||||
NereidUser,
|
||||
ContactMechanism,
|
||||
NereidUser,
|
||||
Permission,
|
||||
UserPermission,
|
||||
URLMap,
|
||||
|
@ -25,8 +27,8 @@ def register():
|
|||
WebsiteCurrency,
|
||||
NereidStaticFolder,
|
||||
NereidStaticFile,
|
||||
Language,
|
||||
Currency,
|
||||
ContextProcessors,
|
||||
Language,
|
||||
module='nereid', type_='model'
|
||||
)
|
||||
|
|
|
@ -5,6 +5,7 @@ from nereid import request
|
|||
|
||||
__all__ = ['Currency', 'Language']
|
||||
|
||||
|
||||
class Currency(ModelSQL, ModelView):
|
||||
'''Currency Manipulation for core.'''
|
||||
__name__ = 'currency.currency'
|
||||
|
|
3
i18n.py
3
i18n.py
|
@ -61,7 +61,7 @@ def gettext(string, **variables):
|
|||
|
||||
|
||||
def ngettext(singular, plural, n, **variables):
|
||||
"""Translates a string with the current locale and passes it to the
|
||||
"""Translates a string with the current locale and passes it to the
|
||||
ngettext API of the translations object
|
||||
"""
|
||||
t = get_translations()
|
||||
|
@ -70,6 +70,7 @@ def ngettext(singular, plural, n, **variables):
|
|||
return (plural if n > 1 else singular) % variables
|
||||
return t.ungettext(singular, plural, n) % variables
|
||||
|
||||
|
||||
def make_lazy_gettext(lookup_func):
|
||||
"""Creates a lazy gettext function dispatches to a gettext
|
||||
function as returned by `lookup_func`.
|
||||
|
|
144
party.py
144
party.py
|
@ -43,7 +43,7 @@ class RegistrationForm(Form):
|
|||
"""
|
||||
return get_translations()
|
||||
|
||||
name = TextField(_('Name'), [validators.Required(),])
|
||||
name = TextField(_('Name'), [validators.Required(), ])
|
||||
email = TextField(_('e-mail'), [validators.Required(), validators.Email()])
|
||||
password = PasswordField(_('New Password'), [
|
||||
validators.Required(),
|
||||
|
@ -68,12 +68,12 @@ class AddressForm(Form):
|
|||
"""
|
||||
return get_translations()
|
||||
|
||||
name = TextField(_('Name'), [validators.Required(),])
|
||||
street = TextField(_('Street'), [validators.Required(),])
|
||||
name = TextField(_('Name'), [validators.Required(), ])
|
||||
street = TextField(_('Street'), [validators.Required(), ])
|
||||
streetbis = TextField(_('Street (Bis)'))
|
||||
zip = TextField(_('Post Code'), [validators.Required(),])
|
||||
city = TextField(_('City'), [validators.Required(),])
|
||||
country = SelectField(_('Country'), [validators.Required(),], coerce=int)
|
||||
zip = TextField(_('Post Code'), [validators.Required(), ])
|
||||
city = TextField(_('City'), [validators.Required(), ])
|
||||
country = SelectField(_('Country'), [validators.Required(), ], coerce=int)
|
||||
subdivision = IntegerField(_('State/County'), [validators.Required()])
|
||||
email = TextField(_('Email'))
|
||||
phone = TextField(_('Phone'))
|
||||
|
@ -137,7 +137,7 @@ class Address(ModelSQL, ModelView):
|
|||
form = AddressForm(request.form, name=request.nereid_user.display_name)
|
||||
countries = [
|
||||
(c.id, c.name) for c in request.nereid_website.countries
|
||||
]
|
||||
]
|
||||
form.country.choices = countries
|
||||
if address not in (a.id for a in request.nereid_user.party.addresses):
|
||||
address = None
|
||||
|
@ -153,7 +153,7 @@ class Address(ModelSQL, ModelView):
|
|||
'subdivision': form.subdivision.data,
|
||||
'email': form.email.data,
|
||||
'phone': form.phone.data,
|
||||
})
|
||||
})
|
||||
else:
|
||||
cls.create([{
|
||||
'name': form.name.data,
|
||||
|
@ -166,7 +166,7 @@ class Address(ModelSQL, ModelView):
|
|||
'party': request.nereid_user.party.id,
|
||||
'email': form.email.data,
|
||||
'phone': form.phone.data,
|
||||
}])
|
||||
}])
|
||||
return redirect(url_for('party.address.view_address'))
|
||||
elif request.method == 'GET' and address:
|
||||
# Its an edit of existing address, prefill data
|
||||
|
@ -192,7 +192,6 @@ class Address(ModelSQL, ModelView):
|
|||
return render_template('address.jinja')
|
||||
|
||||
|
||||
|
||||
class Party(ModelSQL, ModelView):
|
||||
"Party"
|
||||
__name__ = 'party.party'
|
||||
|
@ -202,14 +201,17 @@ class Party(ModelSQL, ModelView):
|
|||
|
||||
class ProfileForm(Form):
|
||||
"""User Profile Form"""
|
||||
display_name = TextField('Display Name', [validators.Required(),],
|
||||
display_name = TextField(
|
||||
'Display Name', [validators.Required(), ],
|
||||
description="Your display name"
|
||||
)
|
||||
timezone = SelectField('Timezone',
|
||||
choices = [(tz, tz) for tz in pytz.common_timezones],
|
||||
timezone = SelectField(
|
||||
'Timezone',
|
||||
choices=[(tz, tz) for tz in pytz.common_timezones],
|
||||
coerce=unicode, description="Your timezone"
|
||||
)
|
||||
email = TextField('Email', [validators.Required(), validators.Email()],
|
||||
email = TextField(
|
||||
'Email', [validators.Required(), validators.Email()],
|
||||
description="Your Login Email. This Cannot be edited."
|
||||
)
|
||||
|
||||
|
@ -221,8 +223,10 @@ class NereidUser(ModelSQL, ModelView):
|
|||
__name__ = "nereid.user"
|
||||
_rec_name = 'display_name'
|
||||
|
||||
party = fields.Many2One('party.party', 'Party', required=True,
|
||||
ondelete='CASCADE', select=1)
|
||||
party = fields.Many2One(
|
||||
'party.party', 'Party', required=True,
|
||||
ondelete='CASCADE', select=1
|
||||
)
|
||||
|
||||
display_name = fields.Char('Display Name', required=True)
|
||||
|
||||
|
@ -234,15 +238,15 @@ class NereidUser(ModelSQL, ModelView):
|
|||
password = fields.Sha('Password')
|
||||
|
||||
#: The salt which was used to make the hash is separately
|
||||
#: stored. Needed for
|
||||
#: stored. Needed for
|
||||
salt = fields.Char('Salt', size=8)
|
||||
|
||||
#: A unique activation code required to match the user's request
|
||||
#: for activation of the account.
|
||||
activation_code = fields.Char('Unique Activation Code')
|
||||
|
||||
# The company of the website(s) to which the user is affiliated. This
|
||||
# allows websites of the same company to share authentication/users. It
|
||||
# The company of the website(s) to which the user is affiliated. This
|
||||
# allows websites of the same company to share authentication/users. It
|
||||
# does not make business or technical sense to have website of multiple
|
||||
# companies share the authentication.
|
||||
#
|
||||
|
@ -254,8 +258,10 @@ class NereidUser(ModelSQL, ModelView):
|
|||
[(x, x) for x in pytz.common_timezones], 'Timezone', translate=False
|
||||
)
|
||||
|
||||
permissions = fields.Many2Many('nereid.permission-nereid.user',
|
||||
'nereid_user', 'permission', 'Permissions')
|
||||
permissions = fields.Many2Many(
|
||||
'nereid.permission-nereid.user',
|
||||
'nereid_user', 'permission', 'Permissions'
|
||||
)
|
||||
|
||||
def get_permissions(self):
|
||||
"""
|
||||
|
@ -265,19 +271,29 @@ class NereidUser(ModelSQL, ModelView):
|
|||
# everytime.
|
||||
return frozenset([p.value for p in self.permissions])
|
||||
|
||||
def has_permissions(self, permissions):
|
||||
"""Check if the user has required permissions for access
|
||||
def has_permissions(self, perm_all=None, perm_any=None):
|
||||
"""Check if the user has all required permissions in perm_all and
|
||||
has any permission from perm_any for access
|
||||
|
||||
:param permissions: A set/frozenset of permission values/keywords
|
||||
:param perm_all: A set/frozenset of all permission values/keywords.
|
||||
:param perm_any: A set/frozenset of any permission values/keywords.
|
||||
|
||||
:return: True/False
|
||||
"""
|
||||
if not isinstance(permissions, (set, frozenset)):
|
||||
permissions = frozenset(permissions)
|
||||
current_user_permissions = self.get_permissions()
|
||||
if permissions.issubset(current_user_permissions):
|
||||
if not perm_all and not perm_any:
|
||||
# Access allowed if no permission is required
|
||||
return True
|
||||
return False
|
||||
if not isinstance(perm_all, (set, frozenset)):
|
||||
perm_all = frozenset(perm_all if perm_all else [])
|
||||
if not isinstance(perm_any, (set, frozenset)):
|
||||
perm_any = frozenset(perm_any if perm_any else [])
|
||||
current_user_permissions = self.get_permissions()
|
||||
|
||||
if perm_all and not perm_all.issubset(current_user_permissions):
|
||||
return False
|
||||
if perm_any and not perm_any.intersection(current_user_permissions):
|
||||
return False
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def default_timezone():
|
||||
|
@ -293,7 +309,7 @@ class NereidUser(ModelSQL, ModelView):
|
|||
cls._sql_constraints += [
|
||||
('unique_email_company', 'UNIQUE(email, company)',
|
||||
'Email must be unique in a company'),
|
||||
]
|
||||
]
|
||||
|
||||
def _activate(self, activation_code):
|
||||
"""
|
||||
|
@ -307,7 +323,7 @@ class NereidUser(ModelSQL, ModelView):
|
|||
:return: True if the activation code was correct
|
||||
"""
|
||||
assert self.activation_code == activation_code, \
|
||||
'Invalid Activation Code'
|
||||
'Invalid Activation Code'
|
||||
return self.write([self], {'activation_code': None})
|
||||
|
||||
@staticmethod
|
||||
|
@ -345,9 +361,11 @@ class NereidUser(ModelSQL, ModelView):
|
|||
existing = cls.search([
|
||||
('email', '=', request.form['email']),
|
||||
('company', '=', request.nereid_website.company.id),
|
||||
])
|
||||
]
|
||||
)
|
||||
if existing:
|
||||
flash(_('A registration already exists with this email. '
|
||||
flash(_(
|
||||
'A registration already exists with this email. '
|
||||
'Please contact customer care')
|
||||
)
|
||||
else:
|
||||
|
@ -359,7 +377,8 @@ class NereidUser(ModelSQL, ModelView):
|
|||
'email': registration_form.email.data,
|
||||
'password': registration_form.password.data,
|
||||
'company': request.nereid_website.company.id,
|
||||
})
|
||||
}
|
||||
)
|
||||
nereid_user.save()
|
||||
nereid_user.create_act_code()
|
||||
registration.send(nereid_user)
|
||||
|
@ -381,9 +400,9 @@ class NereidUser(ModelSQL, ModelView):
|
|||
"""
|
||||
email_message = render_email(
|
||||
CONFIG['smtp_from'], self.email, _('Account Activation'),
|
||||
text_template = 'emails/activation-text.jinja',
|
||||
html_template = 'emails/activation-html.jinja',
|
||||
nereid_user = self
|
||||
text_template='emails/activation-text.jinja',
|
||||
html_template='emails/activation-html.jinja',
|
||||
nereid_user=self
|
||||
)
|
||||
server = get_smtp_server()
|
||||
server.sendmail(
|
||||
|
@ -411,7 +430,7 @@ class NereidUser(ModelSQL, ModelView):
|
|||
)
|
||||
flash(
|
||||
_('Your password has been successfully changed! '
|
||||
'Please login again')
|
||||
'Please login again')
|
||||
)
|
||||
session.pop('user')
|
||||
return redirect(url_for('nereid.website.login'))
|
||||
|
@ -451,9 +470,9 @@ class NereidUser(ModelSQL, ModelView):
|
|||
{'password': form.password.data}
|
||||
)
|
||||
session.pop('allow_new_password')
|
||||
flash(_('Your password has been successfully changed! '
|
||||
'Please login again')
|
||||
)
|
||||
flash(_(
|
||||
'Your password has been successfully changed! '
|
||||
'Please login again'))
|
||||
session.pop('user')
|
||||
return redirect(url_for('nereid.website.login'))
|
||||
|
||||
|
@ -513,10 +532,12 @@ class NereidUser(ModelSQL, ModelView):
|
|||
the link, he can change his password.
|
||||
"""
|
||||
if request.method == 'POST':
|
||||
user_ids = cls.search([
|
||||
('email', '=', request.form['email']),
|
||||
('company', '=', request.nereid_website.company.id),
|
||||
])
|
||||
user_ids = cls.search(
|
||||
[
|
||||
('email', '=', request.form['email']),
|
||||
('company', '=', request.nereid_website.company.id),
|
||||
]
|
||||
)
|
||||
|
||||
if not user_ids or not request.form['email']:
|
||||
flash(_('Invalid email address'))
|
||||
|
@ -527,7 +548,7 @@ class NereidUser(ModelSQL, ModelView):
|
|||
nereid_user.create_act_code("reset")
|
||||
nereid_user.send_reset_email()
|
||||
flash(_('An email has been sent to your account for resetting'
|
||||
' your credentials'))
|
||||
' your credentials'))
|
||||
return redirect(url_for('nereid.website.login'))
|
||||
|
||||
return render_template('reset-password.jinja')
|
||||
|
@ -540,9 +561,9 @@ class NereidUser(ModelSQL, ModelView):
|
|||
"""
|
||||
email_message = render_email(
|
||||
CONFIG['smtp_from'], self.email, _('Account Password Reset'),
|
||||
text_template = 'emails/reset-text.jinja',
|
||||
html_template = 'emails/reset-html.jinja',
|
||||
nereid_user = self
|
||||
text_template='emails/reset-text.jinja',
|
||||
html_template='emails/reset-html.jinja',
|
||||
nereid_user=self
|
||||
)
|
||||
server = get_smtp_server()
|
||||
server.sendmail(
|
||||
|
@ -597,10 +618,10 @@ class NereidUser(ModelSQL, ModelView):
|
|||
# A new account with activation pending
|
||||
current_app.logger.debug('%s not activated' % email)
|
||||
flash(_("Your account has not been activated yet!"))
|
||||
return False # False so to avoid `invalid credentials` flash
|
||||
return False # False so to avoid `invalid credentials` flash
|
||||
|
||||
if user.match_password(password):
|
||||
# Reset any reset activation code that might be there since its a
|
||||
# Reset any reset activation code that might be there since its a
|
||||
# successful login with the old password
|
||||
if user.activation_code:
|
||||
cls.write([user], {'activation_code': None})
|
||||
|
@ -804,14 +825,14 @@ class ContactMechanism(ModelSQL, ModelView):
|
|||
return redirect(request.referrer)
|
||||
|
||||
|
||||
|
||||
class Permission(ModelSQL, ModelView):
|
||||
"Nereid Permissions"
|
||||
__name__ = 'nereid.permission'
|
||||
|
||||
name = fields.Char('Name', required=True, select=True)
|
||||
value = fields.Char('Value', required=True, select=True)
|
||||
nereid_users = fields.Many2Many('nereid.permission-nereid.user',
|
||||
value = fields.Char('Value', required=True, select=True)
|
||||
nereid_users = fields.Many2Many(
|
||||
'nereid.permission-nereid.user',
|
||||
'permission', 'nereid_user', 'Nereid Users'
|
||||
)
|
||||
|
||||
|
@ -821,15 +842,18 @@ class Permission(ModelSQL, ModelView):
|
|||
cls._sql_constraints += [
|
||||
('unique_value', 'UNIQUE(value)',
|
||||
'Permissions must be unique by value'),
|
||||
]
|
||||
|
||||
]
|
||||
|
||||
|
||||
class UserPermission(ModelSQL):
|
||||
"Nereid User Permissions"
|
||||
__name__ = 'nereid.permission-nereid.user'
|
||||
|
||||
permission = fields.Many2One('nereid.permission', 'Permission',
|
||||
ondelete='CASCADE', select=True, required=True)
|
||||
nereid_user = fields.Many2One('nereid.user', 'User',
|
||||
ondelete='CASCADE', select=True, required=True)
|
||||
permission = fields.Many2One(
|
||||
'nereid.permission', 'Permission',
|
||||
ondelete='CASCADE', select=True, required=True
|
||||
)
|
||||
nereid_user = fields.Many2One(
|
||||
'nereid.user', 'User',
|
||||
ondelete='CASCADE', select=True, required=True
|
||||
)
|
||||
|
|
76
routing.py
76
routing.py
|
@ -1,6 +1,5 @@
|
|||
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
from ast import literal_eval
|
||||
|
||||
import pytz
|
||||
from werkzeug import abort, redirect
|
||||
|
@ -11,7 +10,6 @@ from nereid.globals import session, request
|
|||
from nereid.helpers import login_required, key_from_list, get_flashed_messages
|
||||
from nereid.signals import login, failed_login, logout
|
||||
from trytond.model import ModelView, ModelSQL, fields
|
||||
from trytond.backend import TableHandler
|
||||
from trytond.transaction import Transaction
|
||||
from trytond.pool import Pool
|
||||
|
||||
|
@ -42,7 +40,7 @@ class URLMap(ModelSQL, ModelView):
|
|||
:param charset: default value - utf-8
|
||||
:param strict_slashes: Boolean field if / in url map is taken seriously
|
||||
:param unique_urls: Enable `redirect_defaults` in the URL Map and
|
||||
redirects the defaults to the URL
|
||||
redirects the defaults to the URL
|
||||
"""
|
||||
__name__ = "nereid.url_map"
|
||||
|
||||
|
@ -75,10 +73,10 @@ class URLMap(ModelSQL, ModelView):
|
|||
def get_rules_arguments(self):
|
||||
"""
|
||||
Constructs a list of dictionary of arguments needed
|
||||
for URL Rule construction. A wrapper around the
|
||||
for URL Rule construction. A wrapper around the
|
||||
URL RULE get_rule_arguments
|
||||
"""
|
||||
rule_args = [ ]
|
||||
rule_args = []
|
||||
for rule in self.rules:
|
||||
rule_args.append(rule.get_rule_arguments())
|
||||
return rule_args
|
||||
|
@ -92,8 +90,8 @@ class LoginForm(Form):
|
|||
|
||||
class WebSite(ModelSQL, ModelView):
|
||||
"""
|
||||
One of the most powerful features of Nereid is the ability to
|
||||
manage multiple websites from one back-end. A web site in nereid
|
||||
One of the most powerful features of Nereid is the ability to
|
||||
manage multiple websites from one back-end. A web site in nereid
|
||||
represents a collection or URLs, settings.
|
||||
|
||||
:param name: Name of the web site
|
||||
|
@ -112,7 +110,7 @@ class WebSite(ModelSQL, ModelView):
|
|||
#: other settings for the website. Needs to be unique
|
||||
name = fields.Char('Name', required=True, select=True)
|
||||
|
||||
#: The URLMap is made as a different object which functions as a
|
||||
#: The URLMap is made as a different object which functions as a
|
||||
#: collection of Rules. This will allow easy replication of sites
|
||||
#: which perform with same URL structures but different templates
|
||||
url_map = fields.Many2One('nereid.url_map', 'URL Map', required=True)
|
||||
|
@ -135,8 +133,10 @@ class WebSite(ModelSQL, ModelView):
|
|||
'website', 'currency', 'Currencies Available')
|
||||
|
||||
#: Default language
|
||||
default_language = fields.Many2One('ir.lang', 'Default Language',
|
||||
required=True)
|
||||
default_language = fields.Many2One(
|
||||
'ir.lang', 'Default Language',
|
||||
required=True
|
||||
)
|
||||
|
||||
#: The res.user with which the nereid application will be loaded
|
||||
#: .. versionadded: 0.3
|
||||
|
@ -172,10 +172,10 @@ class WebSite(ModelSQL, ModelView):
|
|||
"""
|
||||
Return the list of countries in JSON
|
||||
"""
|
||||
return jsonify(result = [
|
||||
{'key': c.id, 'value': c.name} \
|
||||
for c in request.nereid_website.countries
|
||||
])
|
||||
return jsonify(result=[
|
||||
{'key': c.id, 'value': c.name}
|
||||
for c in request.nereid_website.countries
|
||||
])
|
||||
|
||||
@staticmethod
|
||||
def subdivision_list():
|
||||
|
@ -189,11 +189,11 @@ class WebSite(ModelSQL, ModelView):
|
|||
Subdivision = Pool().get('country.subdivision')
|
||||
subdivisions = Subdivision.search([('country', '=', country)])
|
||||
return jsonify(
|
||||
result = [{
|
||||
result=[{
|
||||
'id': s.id,
|
||||
'name': s.name,
|
||||
'code': s.code,
|
||||
} for s in subdivisions
|
||||
} for s in subdivisions
|
||||
]
|
||||
)
|
||||
|
||||
|
@ -240,12 +240,12 @@ class WebSite(ModelSQL, ModelView):
|
|||
# Result can be the following:
|
||||
# 1 - Browse record of User (successful login)
|
||||
# 2 - None - Login failure without message
|
||||
# 3 - Any other false value (no message is shown. useful if you
|
||||
# 3 - Any other false value (no message is shown. useful if you
|
||||
# want to handle the message shown to user)
|
||||
if result:
|
||||
# NOTE: Translators leave %s as such
|
||||
flash(_("You are now logged in. Welcome %(name)s",
|
||||
name=result.display_name))
|
||||
name=result.display_name))
|
||||
session['user'] = result.id
|
||||
login.send()
|
||||
if request.is_xhr:
|
||||
|
@ -285,8 +285,8 @@ class WebSite(ModelSQL, ModelView):
|
|||
data into the context
|
||||
"""
|
||||
return dict(
|
||||
user = request.nereid_user,
|
||||
party = request.nereid_user.party,
|
||||
user=request.nereid_user,
|
||||
party=request.nereid_user.party,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
|
@ -298,15 +298,15 @@ class WebSite(ModelSQL, ModelView):
|
|||
"""Returns available currencies for current site
|
||||
|
||||
.. note::
|
||||
A special method is required so that the fetch can be speeded up,
|
||||
by pushing the categories to the central cache which cannot be
|
||||
A special method is required so that the fetch can be speeded up,
|
||||
by pushing the categories to the central cache which cannot be
|
||||
done directly on a browse node.
|
||||
"""
|
||||
cache_key = key_from_list([
|
||||
Transaction().cursor.dbname,
|
||||
Transaction().user,
|
||||
'nereid.website.get_currencies',
|
||||
])
|
||||
])
|
||||
# The website is automatically appended to the cache prefix
|
||||
rv = cache.get(cache_key)
|
||||
if rv is None:
|
||||
|
@ -314,8 +314,9 @@ class WebSite(ModelSQL, ModelView):
|
|||
'id': c.id,
|
||||
'name': c.name,
|
||||
'symbol': c.symbol,
|
||||
} for c in self.currencies]
|
||||
cache.set(cache_key, rv, 60*60)
|
||||
} for c in self.currencies
|
||||
]
|
||||
cache.set(cache_key, rv, 60 * 60)
|
||||
return rv
|
||||
|
||||
@staticmethod
|
||||
|
@ -346,7 +347,6 @@ class WebSite(ModelSQL, ModelView):
|
|||
return jsonify(status=cls._user_status())
|
||||
|
||||
|
||||
|
||||
class URLRule(ModelSQL, ModelView):
|
||||
"""
|
||||
URL Rule
|
||||
|
@ -381,8 +381,8 @@ class URLRule(ModelSQL, ModelView):
|
|||
|
||||
:param defaults: Defaults of the URL (O2M - URLRuleDefaults)
|
||||
|
||||
:param method: POST, GET,
|
||||
:param only_for_generation: URL will not be mapped, but can be used
|
||||
:param method: POST, GET,
|
||||
:param only_for_generation: URL will not be mapped, but can be used
|
||||
for URL generation. Example for static pages, where content
|
||||
delivery is managed by apache, but URL generation is necessary
|
||||
:param redirect_to: (M2O self) Another URL to which the redirect has to
|
||||
|
@ -450,13 +450,13 @@ class URLRule(ModelSQL, ModelView):
|
|||
[(i.key, i.value) for i in self.defaults]
|
||||
)
|
||||
return {
|
||||
'rule': self.rule,
|
||||
'endpoint': self.endpoint,
|
||||
'methods': self.get_http_methods(),
|
||||
'build_only': self.only_for_genaration,
|
||||
'defaults': defaults,
|
||||
'redirect_to': self.redirect_to or None,
|
||||
}
|
||||
'rule': self.rule,
|
||||
'endpoint': self.endpoint,
|
||||
'methods': self.get_http_methods(),
|
||||
'build_only': self.only_for_genaration,
|
||||
'defaults': defaults,
|
||||
'redirect_to': self.redirect_to or None,
|
||||
}
|
||||
|
||||
|
||||
class URLRuleDefaults(ModelSQL, ModelView):
|
||||
|
@ -472,8 +472,10 @@ class URLRuleDefaults(ModelSQL, ModelView):
|
|||
|
||||
key = fields.Char('Key', required=True, select=True)
|
||||
value = fields.Char('Value', required=True, select=True)
|
||||
rule = fields.Many2One('nereid.url_rule', 'Rule', required=True,
|
||||
select=True)
|
||||
rule = fields.Many2One(
|
||||
'nereid.url_rule', 'Rule', required=True,
|
||||
select=True
|
||||
)
|
||||
|
||||
|
||||
class WebsiteCountry(ModelSQL):
|
||||
|
|
|
@ -91,8 +91,9 @@ class NereidStaticFile(ModelSQL, ModelView):
|
|||
], 'File Type')
|
||||
|
||||
#: URL of the remote file if the :attr:`type` is remote
|
||||
remote_path = fields.Char('Remote File', select=True, translate=True,
|
||||
states = {
|
||||
remote_path = fields.Char(
|
||||
'Remote File', select=True, translate=True,
|
||||
states={
|
||||
'required': Equal(Eval('type'), 'remote'),
|
||||
'invisible': Not(Equal(Eval('type'), 'remote'))
|
||||
}
|
||||
|
@ -110,7 +111,7 @@ class NereidStaticFile(ModelSQL, ModelView):
|
|||
|
||||
#: URL that can be used to idenfity the resource. Note that the value
|
||||
#: of this field is available only when called within a request context.
|
||||
#: In other words the URL is valid only when called in a nereid request.
|
||||
#: In other words the URL is valid only when called in a nereid request.
|
||||
url = fields.Function(fields.Char('URL'), 'get_url')
|
||||
|
||||
@classmethod
|
||||
|
@ -211,10 +212,10 @@ class NereidStaticFile(ModelSQL, ModelView):
|
|||
:return: File path
|
||||
"""
|
||||
return os.path.abspath(
|
||||
os.path.join(
|
||||
self.get_nereid_base_path(),
|
||||
self.folder.folder_name, self.name
|
||||
)) \
|
||||
os.path.join(
|
||||
self.get_nereid_base_path(),
|
||||
self.folder.folder_name, self.name
|
||||
)) \
|
||||
if self.type == 'local' else self.remote_path
|
||||
|
||||
def check_file_name(self):
|
||||
|
@ -243,7 +244,7 @@ class NereidStaticFile(ModelSQL, ModelView):
|
|||
files = cls.search([
|
||||
('folder.folder_name', '=', folder),
|
||||
('name', '=', name)
|
||||
])
|
||||
])
|
||||
if not files:
|
||||
abort(404)
|
||||
return send_file(files[0].file_path)
|
||||
|
|
14
template.py
14
template.py
|
@ -11,11 +11,15 @@ class ContextProcessors(ModelSQL, ModelView):
|
|||
__name__ = 'nereid.template.context_processor'
|
||||
_rec_name = 'method'
|
||||
|
||||
method = fields.Char('Method', required=True,
|
||||
help="Context processor method in <model>.<method>")
|
||||
model = fields.Char('Model',
|
||||
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")
|
||||
" the model are called"
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_processors(cls):
|
||||
|
@ -23,7 +27,7 @@ class ContextProcessors(ModelSQL, ModelView):
|
|||
Return the list of processors. Separate function
|
||||
since its important to have caching on this
|
||||
"""
|
||||
result = { }
|
||||
result = {}
|
||||
ctx_processors = cls.search([])
|
||||
for ctx_proc in ctx_processors:
|
||||
model, method = ctx_proc.method.rsplit('.', 1)
|
||||
|
|
|
@ -36,7 +36,6 @@ class TestNereid(unittest.TestCase):
|
|||
test_depends()
|
||||
|
||||
|
||||
|
||||
def suite():
|
||||
test_suite = trytond.tests.test_tryton.suite()
|
||||
test_suite.addTests(
|
||||
|
|
|
@ -40,15 +40,15 @@ class TestAddress(NereidTestCase):
|
|||
self.templates = {
|
||||
'home.jinja': '{{get_flashed_messages()}}',
|
||||
'login.jinja':
|
||||
'{{ login_form.errors }} {{get_flashed_messages()}}',
|
||||
'{{ login_form.errors }} {{get_flashed_messages()}}',
|
||||
'registration.jinja':
|
||||
'{{ form.errors }} {{get_flashed_messages()}}',
|
||||
'{{ form.errors }} {{get_flashed_messages()}}',
|
||||
'reset-password.jinja': '',
|
||||
'change-password.jinja':
|
||||
'{{ change_password_form.errors }}',
|
||||
'{{ change_password_form.errors }}',
|
||||
'address-edit.jinja':
|
||||
'Address Edit {% if address %}ID:{{ address.id }}{% endif %}'
|
||||
'{{ form.errors }}',
|
||||
'Address Edit {% if address %}ID:{{ address.id }}{% endif %}'
|
||||
'{{ form.errors }}',
|
||||
'address.jinja': '',
|
||||
'account.jinja': '',
|
||||
'emails/activation-text.jinja': 'activation-email-text',
|
||||
|
@ -166,8 +166,7 @@ class TestAddress(NereidTestCase):
|
|||
'phone': '1234567890',
|
||||
'country': self.available_countries[0].id,
|
||||
'subdivision': self.country_obj(
|
||||
self.available_countries[0]
|
||||
).subdivisions[0].id,
|
||||
self.available_countries[0]).subdivisions[0].id,
|
||||
}
|
||||
|
||||
with app.test_client() as c:
|
||||
|
@ -178,7 +177,7 @@ class TestAddress(NereidTestCase):
|
|||
'password': 'password',
|
||||
}
|
||||
)
|
||||
self.assertEqual(response.status_code, 302) # Login success
|
||||
self.assertEqual(response.status_code, 302) # Login success
|
||||
|
||||
# Assert that the user has only 1 address, which gets created
|
||||
# automatically with the party
|
||||
|
@ -243,7 +242,7 @@ class TestAddress(NereidTestCase):
|
|||
'password': 'password',
|
||||
}
|
||||
)
|
||||
self.assertEqual(response.status_code, 302) # Login success
|
||||
self.assertEqual(response.status_code, 302) # Login success
|
||||
|
||||
# Assert that the user has only 1 address, which gets created
|
||||
# automatically with the party
|
||||
|
@ -295,11 +294,11 @@ class TestAddress(NereidTestCase):
|
|||
'password': 'password',
|
||||
}
|
||||
)
|
||||
self.assertEqual(response.status_code, 302) # Login success
|
||||
self.assertEqual(response.status_code, 302) # Login success
|
||||
|
||||
with app.test_client() as c:
|
||||
response = c.get('/en_US/view-address')
|
||||
self.assertEqual(response.status_code, 302) # Redir to login
|
||||
self.assertEqual(response.status_code, 302) # Redir to login
|
||||
|
||||
def test_0040_country_list(self):
|
||||
"""
|
||||
|
@ -310,7 +309,7 @@ class TestAddress(NereidTestCase):
|
|||
app = self.get_app()
|
||||
with app.test_client() as c:
|
||||
response = c.get('/en_US/countries')
|
||||
self.assertEqual(response.status_code, 200) # Login success
|
||||
self.assertEqual(response.status_code, 200) # Login success
|
||||
self.assertEqual(len(json.loads(response.data)['result']), 5)
|
||||
|
||||
def test_0050_subdivision_list(self):
|
||||
|
@ -336,7 +335,7 @@ def suite():
|
|||
test_suite = unittest.TestSuite()
|
||||
test_suite.addTests(
|
||||
unittest.TestLoader().loadTestsFromTestCase(TestAddress)
|
||||
)
|
||||
)
|
||||
return test_suite
|
||||
|
||||
|
||||
|
|
|
@ -82,13 +82,13 @@ class TestAuth(NereidTestCase):
|
|||
self.templates = {
|
||||
'home.jinja': '{{get_flashed_messages()}}',
|
||||
'login.jinja':
|
||||
'{{ login_form.errors }} {{get_flashed_messages()}}',
|
||||
'{{ login_form.errors }} {{get_flashed_messages()}}',
|
||||
'registration.jinja':
|
||||
'{{ form.errors }} {{get_flashed_messages()}}',
|
||||
'{{ form.errors }} {{get_flashed_messages()}}',
|
||||
'reset-password.jinja': '{{get_flashed_messages()}}',
|
||||
'change-password.jinja':
|
||||
'''{{ change_password_form.errors }}
|
||||
{{get_flashed_messages()}}''',
|
||||
'''{{ change_password_form.errors }}
|
||||
{{get_flashed_messages()}}''',
|
||||
'address-edit.jinja': 'Address Edit {{ form.errors }}',
|
||||
'address.jinja': '',
|
||||
'account.jinja': '',
|
||||
|
@ -119,7 +119,7 @@ class TestAuth(NereidTestCase):
|
|||
|
||||
with app.test_client() as c:
|
||||
response = c.get('/en_US/registration')
|
||||
self.assertEqual(response.status_code, 200) # GET Request
|
||||
self.assertEqual(response.status_code, 200) # GET Request
|
||||
|
||||
data = {
|
||||
'name': 'Registered User',
|
||||
|
@ -128,7 +128,7 @@ class TestAuth(NereidTestCase):
|
|||
}
|
||||
# Post with missing password
|
||||
response = c.post('/en_US/registration', data=data)
|
||||
self.assertEqual(response.status_code, 200) # Form rejected
|
||||
self.assertEqual(response.status_code, 200) # Form rejected
|
||||
|
||||
data['confirm'] = 'password'
|
||||
response = c.post('/en_US/registration', data=data)
|
||||
|
@ -225,7 +225,7 @@ class TestAuth(NereidTestCase):
|
|||
# Activate the account
|
||||
response = c.get('/en_US/activate-account/%s/%s' % (
|
||||
registered_user.id, registered_user.activation_code
|
||||
)
|
||||
)
|
||||
)
|
||||
self.assertEqual(response.status_code, 302)
|
||||
registered_user = self.nereid_user_obj(registered_user.id)
|
||||
|
@ -306,7 +306,8 @@ class TestAuth(NereidTestCase):
|
|||
})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTrue(
|
||||
"The current password you entered is invalid" in response.data
|
||||
"The current password you entered is invalid"
|
||||
in response.data
|
||||
)
|
||||
|
||||
response = c.post('/en_US/change-password', data={
|
||||
|
@ -318,7 +319,8 @@ class TestAuth(NereidTestCase):
|
|||
response = c.get('/en_US')
|
||||
|
||||
# Login now using new password
|
||||
response = c.post('/en_US/login',
|
||||
response = c.post(
|
||||
'/en_US/login',
|
||||
data={
|
||||
'email': data['email'],
|
||||
'password': 'new-password'
|
||||
|
@ -397,7 +399,8 @@ class TestAuth(NereidTestCase):
|
|||
regd_user = self.nereid_user_obj(regd_user.id)
|
||||
self.assertFalse(regd_user.activation_code)
|
||||
|
||||
response = c.post('/en_US/login',
|
||||
response = c.post(
|
||||
'/en_US/login',
|
||||
data={
|
||||
'email': data['email'],
|
||||
'password': 'wrong-password'
|
||||
|
@ -405,7 +408,8 @@ class TestAuth(NereidTestCase):
|
|||
)
|
||||
self.assertEqual(response.status_code, 200) # Login rejected
|
||||
|
||||
response = c.post('/en_US/login',
|
||||
response = c.post(
|
||||
'/en_US/login',
|
||||
data={
|
||||
'email': data['email'],
|
||||
'password': 'reset-password'
|
||||
|
@ -509,6 +513,7 @@ class TestAuth(NereidTestCase):
|
|||
self.nereid_user_obj.write(
|
||||
[self.guest_user], {'permissions': [('set', [perm_admin])]}
|
||||
)
|
||||
|
||||
@permissions_required(['admin'])
|
||||
def test_permission_2():
|
||||
return True
|
||||
|
@ -594,6 +599,67 @@ class TestAuth(NereidTestCase):
|
|||
response = c.get('/en_US/me')
|
||||
self.assertEqual(response.data, 'Regd User')
|
||||
|
||||
def test_0100_has_permission(self):
|
||||
'''
|
||||
Test the functionality of has_permissions
|
||||
'''
|
||||
with Transaction().start(DB_NAME, USER, CONTEXT):
|
||||
self.setup_defaults()
|
||||
p1, p2, p3, p4 = self.nereid_permission_obj.create([
|
||||
{'name': 'p1', 'value': 'nereid.perm1'},
|
||||
{'name': 'p2', 'value': 'nereid.perm2'},
|
||||
{'name': 'p3', 'value': 'nereid.perm3'},
|
||||
{'name': 'p4', 'value': 'nereid.perm4'},
|
||||
])
|
||||
self.nereid_user_obj.write(
|
||||
[self.guest_user],
|
||||
{
|
||||
'permissions': [
|
||||
('add', [p1, p2])
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
# all = [], any = [] = True
|
||||
self.assertTrue(self.guest_user.has_permissions())
|
||||
|
||||
# all = [p1, p2], any = [] == True
|
||||
self.assertTrue(self.guest_user.has_permissions(
|
||||
perm_all=[p1.value, p2.value]
|
||||
))
|
||||
|
||||
# all = [p1, p2], any = [p3, p4] == False
|
||||
self.assertFalse(self.guest_user.has_permissions(
|
||||
perm_all=[p1.value, p2.value],
|
||||
perm_any=[p3.value, p4.value]
|
||||
))
|
||||
|
||||
# all = [p1, p3], any = [] == False
|
||||
self.assertFalse(self.guest_user.has_permissions(
|
||||
perm_all=[p1.value, p3.value],
|
||||
))
|
||||
|
||||
# all = [p1, p3], any = [p1, p3, p4] == False
|
||||
self.assertFalse(self.guest_user.has_permissions(
|
||||
perm_all=[p1.value, p3.value],
|
||||
perm_any=[p1.value, p3.value, p4.value]
|
||||
))
|
||||
|
||||
# all = [p1, p2], any = [p1, p3, p4] == True
|
||||
self.assertTrue(self.guest_user.has_permissions(
|
||||
perm_all=[p1.value, p2.value],
|
||||
perm_any=[p1.value, p3.value, p4.value]
|
||||
))
|
||||
|
||||
# all = [], any = [p1, p2, p3] == True
|
||||
self.assertTrue(self.guest_user.has_permissions(
|
||||
perm_any=[p1.value, p2.value, p3.value]
|
||||
))
|
||||
|
||||
# all = [], any = [p3, p4] == False
|
||||
self.assertFalse(self.guest_user.has_permissions(
|
||||
perm_any=[p3.value, p4.value]
|
||||
))
|
||||
|
||||
|
||||
def suite():
|
||||
|
@ -601,7 +667,7 @@ def suite():
|
|||
test_suite = unittest.TestSuite()
|
||||
test_suite.addTests(
|
||||
unittest.TestLoader().loadTestsFromTestCase(TestAuth)
|
||||
)
|
||||
)
|
||||
return test_suite
|
||||
|
||||
|
||||
|
|
|
@ -121,7 +121,6 @@ class TestCurrency(NereidTestCase):
|
|||
self.currency_obj.convert(Decimal('100')), Decimal('100')
|
||||
)
|
||||
|
||||
|
||||
def test_0020_currency_from_language(self):
|
||||
"""
|
||||
Set the currency for the language and check if the currency
|
||||
|
@ -151,7 +150,7 @@ def suite():
|
|||
test_suite = unittest.TestSuite()
|
||||
test_suite.addTests(
|
||||
unittest.TestLoader().loadTestsFromTestCase(TestCurrency)
|
||||
)
|
||||
)
|
||||
return test_suite
|
||||
|
||||
|
||||
|
|
|
@ -111,7 +111,7 @@ def suite():
|
|||
test_suite = unittest.TestSuite()
|
||||
test_suite.addTests(
|
||||
unittest.TestLoader().loadTestsFromTestCase(TestI18N)
|
||||
)
|
||||
)
|
||||
return test_suite
|
||||
|
||||
|
||||
|
|
|
@ -49,10 +49,10 @@ class TestStaticFile(NereidTestCase):
|
|||
|
||||
self.templates = {
|
||||
'home.jinja':
|
||||
'''
|
||||
{% set static_file = static_file_obj(static_file_id) %}
|
||||
{{ static_file.url }}
|
||||
''',
|
||||
'''
|
||||
{% set static_file = static_file_obj(static_file_id) %}
|
||||
{{ static_file.url }}
|
||||
''',
|
||||
}
|
||||
|
||||
def setup_defaults(self):
|
||||
|
@ -149,7 +149,7 @@ class TestStaticFile(NereidTestCase):
|
|||
home_func = functools.partial(home_func, file_id=file.id)
|
||||
c.application.view_functions[
|
||||
'nereid.website.home'] = new.instancemethod(
|
||||
home_func, self.nereid_website_obj
|
||||
home_func, self.nereid_website_obj
|
||||
)
|
||||
self.nereid_website_obj.home = new.instancemethod(
|
||||
home_func, self.nereid_website_obj
|
||||
|
@ -190,7 +190,7 @@ class TestStaticFile(NereidTestCase):
|
|||
home_func = functools.partial(home_func, file_id=file.id)
|
||||
c.application.view_functions[
|
||||
'nereid.website.home'] = new.instancemethod(
|
||||
home_func, self.nereid_website_obj
|
||||
home_func, self.nereid_website_obj
|
||||
)
|
||||
self.nereid_website_obj.home = new.instancemethod(
|
||||
home_func, self.nereid_website_obj
|
||||
|
@ -207,7 +207,7 @@ def suite():
|
|||
test_suite = unittest.TestSuite()
|
||||
test_suite.addTests(
|
||||
unittest.TestLoader().loadTestsFromTestCase(TestStaticFile)
|
||||
)
|
||||
)
|
||||
return test_suite
|
||||
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
[tryton]
|
||||
version=2.8.0.2
|
||||
version=2.8.0.5
|
||||
depends:
|
||||
ir
|
||||
res
|
||||
|
|
Loading…
Reference in New Issue