Skip to content
GitLab
Menu
Projects
Groups
Snippets
/
Help
What's new
7
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Switch to GitLab Next
Sign in / Register
Toggle navigation
Menu
Open sidebar
lino-framework
lino
Commits
d243decf
Commit
d243decf
authored
Jan 04, 2017
by
Luc Saffre
Browse files
http://luc.lino-framework.org/blog/2017/0104.html
parent
19bea378
Changes
21
Hide whitespace changes
Inline
Side-by-side
lino/core/actions.py
View file @
d243decf
...
...
@@ -562,7 +562,6 @@ class Action(Parametrizable, Permittable):
"""
# if not actor.editable and not self.readonly:
# return False
if
self
.
defining_actor
is
not
None
:
# already defined in another actor
return
True
...
...
@@ -625,7 +624,10 @@ class Action(Parametrizable, Permittable):
on an action it is a instance method.
Note that this method is not called for actions which are rendered
in a toolbar (:srcref:`docs/tickets/105`)
in a toolbar (:ticket:`1336`).
Usage examples:
:class:`lino.modlib.users.actions.SendWelcomeMail`
"""
...
...
lino/core/actors.py
View file @
d243decf
...
...
@@ -267,7 +267,6 @@ class Actor(with_metaclass(ActorMetaClass, type('NewBase', (actions.Parametrizab
the user has permission to view the actor or not.
"""
required_roles
=
set
([
SiteUser
])
"""See :attr:`lino.core.permissions.Permittable.required_roles`"""
...
...
lino/core/dbtables.py
View file @
d243decf
# Copyright 2009-201
6
Luc Saffre
# Copyright 2009-201
7
Luc Saffre
# License: BSD (see file COPYING for details)
"""
...
...
@@ -218,22 +218,25 @@ def discover():
if
issubclass
(
rpt
,
frames
.
Frame
)
and
rpt
is
not
frames
.
Frame
:
register_frame
(
rpt
)
logger
.
debug
(
"Instantiate model tables..."
)
for
model
in
get_models
():
# Not getattr but __dict__.get because of the mixins.Listings
# trick.
rpt
=
model
.
__dict__
.
get
(
'_lino_default_table'
,
None
)
# rpt = getattr(model,'_lino_default_table',None)
# logger.debug('20111113 %s._lino_default_table = %s',model,rpt)
if
rpt
is
None
:
rpt
=
table_factory
(
model
)
if
rpt
is
None
:
raise
Exception
(
"table_factory() failed for %r."
%
model
)
register_report
(
rpt
)
rpt
.
class_init
()
# rpt.collect_actions()
model
.
_lino_default_table
=
rpt
# logger.debug("Create default tables...")
# for model in get_models():
# # Note that automatic models (created by ManyToManyField with
# # a `through`) do not yet exist here.
# # Not getattr but __dict__.get because of the mixins.Listings
# # trick:
# rpt = model.__dict__.get('_lino_default_table', None)
# # rpt = getattr(model,'_lino_default_table',None)
# # logger.debug('20111113 %s._lino_default_table = %s',model,rpt)
# if rpt is None:
# rpt = table_factory(model)
# if rpt is None:
# raise Exception("table_factory() failed for %r." % model)
# # print ("20170104 No table for {}, created default table.".format(model))
# register_report(rpt)
# rpt.class_init()
# # rpt.collect_actions()
# model._lino_default_table = rpt
logger
.
debug
(
"Analyze %d slave tables..."
,
len
(
slave_reports
))
for
rpt
in
slave_reports
:
...
...
lino/core/kernel.py
View file @
d243decf
...
...
@@ -422,6 +422,28 @@ class Kernel(object):
a
.
class_init
()
dbtables
.
discover
()
# Create default tables. Every model for which there is no
# table at all, will now get an automatically created default
# table. This includes automatic models created by
# ManyToManyField.
for
model
in
models_list
:
# Not getattr but __dict__.get because of the mixins.Listings
# trick:
rpt
=
model
.
__dict__
.
get
(
'_lino_default_table'
,
None
)
# rpt = getattr(model,'_lino_default_table',None)
# logger.debug('20111113 %s._lino_default_table = %s',model,rpt)
if
rpt
is
None
:
rpt
=
dbtables
.
table_factory
(
model
)
if
rpt
is
None
:
raise
Exception
(
"table_factory() failed for %r."
%
model
)
# print ("20170104 No table for {}, created default table.".format(model))
dbtables
.
register_report
(
rpt
)
rpt
.
class_init
()
# rpt.collect_actions()
model
.
_lino_default_table
=
rpt
#~ choosers.discover()
actions
.
discover_choosers
()
...
...
@@ -443,15 +465,27 @@ class Kernel(object):
site
.
on_each_app
(
'site_setup'
)
# deprecated
# Actor.after_site_setup() is called after the
app
s'
# Actor.after_site_setup() is called after the
plugin
s'
s
# site_setup(). Example: pcsw.site_setup() adds a detail to
# properties.Properties, the base class for
# properties.PropsByGroup. The latter would not install a
# `detail_action` during her after_site_setup() and also would
# never get it later.
# In a first loop we run it on actors who are being used as
# default tables for a model. Because the defining_actor of
# model actions will be the first actor to which they get
# attached.
later
=
[]
for
a
in
actors
.
actors_list
:
if
a
.
model
and
a
==
a
.
model
.
get_default_table
():
a
.
after_site_setup
(
site
)
else
:
later
.
append
(
a
)
for
a
in
later
:
a
.
after_site_setup
(
site
)
#~ site.on_site_startup()
...
...
lino/core/layouts.py
View file @
d243decf
...
...
@@ -620,7 +620,10 @@ class ParamsLayout(BaseLayout):
params_store
=
None
def
get_data_elem
(
self
,
name
):
return
self
.
_datasource
.
get_param_elem
(
name
)
de
=
self
.
_datasource
.
get_param_elem
(
name
)
if
de
is
None
:
de
=
self
.
_datasource
.
get_data_elem
(
name
)
return
de
def
setup_handle
(
self
,
lh
):
# if str(self._datasource) == 'courses.Pupils':
...
...
lino/core/model.py
View file @
d243decf
...
...
@@ -204,9 +204,9 @@ class Model(models.Model):
workflow_state_field
=
None
"""If this is set on a Model, then it will be used as default value
for
:attr:`workflow_state_field
<lino.core.table.Table.workflow_state_field>`
of all tables based
on this Model.
for
:attr:`workflow_state_field
<lino.core.table.Table.workflow_state_field>`
of all tables based
on this Model.
"""
...
...
@@ -726,6 +726,7 @@ class Model(models.Model):
l
=
[]
if
ar
is
not
None
:
actor
=
ar
.
actor
# print(20170102, actor)
state
=
actor
.
get_row_state
(
obj
)
sep
=
''
show
=
True
# whether to show the state
...
...
@@ -745,6 +746,7 @@ class Model(models.Model):
#~ sep = u" \u25b8 "
for
ba
in
actor
.
get_actions
():
assert
ba
.
actor
==
actor
# 20170102
if
ba
.
action
.
show_in_workflow
:
if
actor
.
get_row_permission
(
obj
,
ar
,
state
,
ba
):
if
show
and
isinstance
(
ba
.
action
,
ChangeStateAction
):
...
...
lino/core/permissions.py
View file @
d243decf
...
...
@@ -173,18 +173,19 @@ def make_view_permission_handler_(
# if action.action_name == "export_excel":
# print 20150828, profile.role, required_roles
return
profile
.
has_required_roles
(
required_roles
)
else
:
def
allow
(
action
,
profile
):
return
True
if
not
readonly
:
allow3
=
allow
if
not
readonly
:
allow3
=
allow
def
allow
(
action
,
profile
):
if
not
allow3
(
action
,
profile
):
return
False
if
profile
.
readonly
:
return
False
return
True
else
:
def
allow
(
action
,
profile
):
if
not
allow3
(
action
,
profile
):
return
False
if
profile
.
readonly
:
return
False
return
True
if
debug_permissions
:
# False:
...
...
lino/core/site.py
View file @
d243decf
...
...
@@ -522,8 +522,10 @@ class Site(object):
"""
user_types_module
=
None
"""The full Python path of the **user profiles module** to be used on
this site.
"""The name of the **user profiles module** to be used on this site.
Default value is `None`, meaning that permission control is
inactive: everything is permitted.
This must be set if you want to enable permission control based on
user roles defined in :attr:`Permittable.required_roles
...
...
@@ -531,12 +533,9 @@ class Site(object):
:attr:`UserType.role
<lino.modlib.users.choicelists.UserType.role>`.
Default value is `None`, meaning that role-based permission
control is inactive: every user can see everything.
If set, Lino will import this module during site startup. It is
expected to define application-specific user roles (if necessary)
and to fill the :class:`UserTypes
If set, Lino will import the named module during site startup. It
is expected to define application-specific user roles (if
necessary) and to fill the :class:`UserTypes
<lino.modlib.users.choicelists.UserTypes>` choicelist.
For example::
...
...
@@ -546,7 +545,7 @@ class Site(object):
Examples of such user profiles modules are
:mod:`lino.modlib.users.roles` and
:mod:`lino.projects.
presto
.roles`.
:mod:`lino
_noi
.projects.
noi
.roles`.
"""
...
...
@@ -760,11 +759,11 @@ class Site(object):
"""
anonymous_user_type
=
'000'
"""The user profile to be assigned to the anonymous user
(:class:`AnonymousUser <lino.modlib.users.utils.AnonymousUser>`).
#
anonymous_user_type = '000'
#
"""The user profile to be assigned to the anonymous user
#
(:class:`AnonymousUser <lino.modlib.users.utils.AnonymousUser>`).
"""
#
"""
remote_user_header
=
None
"""The name of the header (set by the web server) that Lino should
...
...
lino/modlib/extjs/config/extjs/index.html
View file @
d243decf
...
...
@@ -137,7 +137,7 @@
{{
ln
}}
{
%-
endfor
-%
}
{
#
anonymous
request
using
permalink
:
forward
request
.
path
as
"
on_login
"
URL
#
}
{
%-
if
settings
.
SITE
.
user_model
and
not
request
.
user
.
authenticated
and
on_ready
-%
}
{
%-
if
settings
.
SITE
.
user_model
and
on_ready
and
not
request
.
user
.
authenticated
and
not
settings
.
SITE
.
plugins
.
users
.
online_registration
-%
}
{
%-
set
on_ready
=
"
Lino.show_login_window(
"
+
py2js
(
request
.
path
)
+
"
)
"
-%
}
{
%-
endif
-%
}
{
#
Render
main
window
#
}
...
...
lino/modlib/extjs/ext_renderer.py
View file @
d243decf
...
...
@@ -895,10 +895,11 @@ class ExtRenderer(HtmlRenderer):
def
lino_js_parts
(
self
):
profile
=
jsgen
.
get_user_profile
()
# return ('genjs',
return
(
'cache'
,
'js'
,
'lino_'
+
profile
.
value
+
'_'
+
translation
.
get_language
()
+
'.js'
)
filename
=
'lino_'
if
profile
is
not
None
:
filename
+=
profile
.
value
+
'_'
filename
+=
translation
.
get_language
()
+
'.js'
return
(
'cache'
,
'js'
,
filename
)
def
linolib_template
(
self
):
env
=
jinja2
.
Environment
(
loader
=
jinja2
.
FileSystemLoader
(
...
...
@@ -1106,6 +1107,8 @@ class ExtRenderer(HtmlRenderer):
yield
" action_name: '%s',"
%
tbl
.
action_name
yield
" ls_url: %s,"
%
py2js
(
dh
.
layout
.
_url
)
yield
" window_title: %s,"
%
py2js
(
tbl
.
label
)
# if not tbl.select_rows:
# yield " default_record_id: -99999,"
yield
" before_row_edit : function(record) {"
for
ln
in
self
.
before_row_edit
(
dh
.
main
):
...
...
lino/modlib/extjs/linoweb.js
View file @
d243decf
...
...
@@ -2500,8 +2500,10 @@ Lino.ActionFormPanel = Ext.extend(Lino.ActionFormPanel, {
pk
=
panel
.
get_current_record
().
id
;
}
if
(
pk
==
undefined
)
{
Lino
.
alert
(
"
Sorry, dialog action without base_params.mk
"
);
return
;
// 20170101 VerifyUser action
pk
=
'
-99998
'
;
// Lino.alert("Sorry, dialog action without base_params.mk");
// return;
}
var
self
=
this
;
// function on_success() { self.get_containing_window().close(); };
...
...
lino/modlib/lino_startup/config/admin_main_base.html
View file @
d243decf
...
...
@@ -58,10 +58,10 @@
<a
href=
"#"
onclick=
"javascript:Lino.show_login_window()"
>
{{_("log in")}}
</a>
{{_("using the
<b>
Log in
</b>
button in the upper right corner.")}}
{% if not request.user.profile.readonly %}
{% if
site.plugins.users.online_registration and
not request.user.profile.readonly %}
{{_("Or {} as a new user.").format(
E.tostring(ar.window_action_button(
site.actors.users.Register
Users
.insert_action,
site.actors.users.Register.insert_action,
label=_("register"), icon_name=None)))}}
{% endif %}
</p>
...
...
lino/modlib/lino_startup/help_texts.py
View file @
d243decf
...
...
@@ -390,6 +390,7 @@ uploaded file in a new browser window."""),
'lino.modlib.uploads.models.MyUploads.model'
:
_
(
"""alias of Upload"""
),
'lino.modlib.uploads.models.UploadsByController.model'
:
_
(
"""alias of Upload"""
),
'lino.modlib.users.Plugin'
:
_
(
"""See /dev/plugins."""
),
'lino.modlib.users.Plugin.online_registration'
:
_
(
"""Whether this site offers online registration of new users."""
),
'lino.modlib.users.actions.SendWelcomeMail'
:
_
(
"""Send a welcome mail to this user."""
),
'lino.modlib.users.actions.ChangePassword'
:
_
(
"""Change the password of this user."""
),
'lino.modlib.users.actions.ChangePassword.current'
:
_
(
"""The current password. Leave empty if the user has no password
...
...
@@ -397,13 +398,13 @@ yet. And SiteAdmin users don't need to specify this at all."""),
'lino.modlib.users.actions.ChangePassword.new1'
:
_
(
"""The new password."""
),
'lino.modlib.users.actions.ChangePassword.new2'
:
_
(
"""The new password a second time. Both passwords must match."""
),
'lino.modlib.users.choicelists.UserType'
:
_
(
"""Base class for all user profiles."""
),
'lino.modlib.users.choicelists.UserType.hidden_languages'
:
_
(
"""A subset of languages which
should be hidden in this user profile. Default value is
hidden_languages. This is
used on multilingual sites with more than 4 or 5 languages."""
),
'lino.modlib.users.choicelists.UserType.role'
:
_
(
"""The role of users having this profile. This is an instance of
'lino.modlib.users.choicelists.UserType.role'
:
_
(
"""The role of users having this type. This is an instance of
<lino.core.roles.UserRole> or some subclass thereof."""
),
'lino.modlib.users.choicelists.UserType.readonly'
:
_
(
"""Whether users with this profile get only write-proteced access."""
),
'lino.modlib.users.choicelists.UserType.readonly'
:
_
(
"""Whether users of this type get only write-proteced access."""
),
'lino.modlib.users.choicelists.UserType.hidden_languages'
:
_
(
"""A subset of languages
which should be hidden for users of this type. Default value
is hidden_languages. This
is used on multilingual sites with more than 4 or 5 languages."""
),
'lino.modlib.users.choicelists.UserTypes'
:
_
(
"""The list of user profiles available on this site."""
),
'lino.modlib.users.choicelists.UserTypes.item_class'
:
_
(
"""alias of UserType"""
),
'lino.modlib.users.choicelists.UserTypes.hidden_languages'
:
_
(
"""Default value for the
...
...
@@ -450,6 +451,7 @@ Leaving this empty means that the user cannot log in."""),
this user."""
),
'lino.modlib.users.models.User.person'
:
_
(
"""A virtual read-only field which returns the Person MTI child corresponding
to the partner (if it exists) and otherwise None."""
),
'lino.modlib.users.models.User.last_login'
:
_
(
"""Not used in Lino."""
),
'lino.modlib.users.models.User.authenticated'
:
_
(
"""This is always True.
See also lino.modlib.users.utils.AnonymousUser.authenticated."""
),
'lino.modlib.users.models.Authority'
:
_
(
"""An Authority is when a user gives another user the right to
...
...
lino/modlib/users/__init__.py
View file @
d243decf
...
...
@@ -29,11 +29,20 @@ from lino.api import ad, _
class
Plugin
(
ad
.
Plugin
):
"See :doc:`/dev/plugins`."
"""See :doc:`/dev/plugins`.
.. attribute:: online_registration
Whether this site offers :ref:`online registration
<online_registration>` of new users.
"""
verbose_name
=
_
(
"Users"
)
needs_plugins
=
[
'lino.modlib.system'
]
online_registration
=
False
def
on_init
(
self
):
super
(
Plugin
,
self
).
on_init
()
self
.
site
.
set_user_model
(
'users.User'
)
...
...
lino/modlib/users/actions.py
View file @
d243decf
...
...
@@ -10,39 +10,82 @@ from builtins import object
from
django.utils.encoding
import
python_2_unicode_compatible
from
django.db
import
models
from
django.utils.translation
import
ugettext_lazy
as
_
from
django.conf
import
settings
from
lino.api
import
dd
,
rt
from
lino.api
import
dd
,
rt
,
_
from
lino.core.roles
import
SiteAdmin
class
SendWelcomeMail
(
dd
.
Action
):
"""Send a welcome mail to this user."""
label
=
_
(
"Welcome mail"
)
show_in_bbar
=
True
show_in_workflow
=
False
if
False
:
# #1336
show_in_bbar
=
True
show_in_workflow
=
False
else
:
show_in_bbar
=
False
show_in_workflow
=
True
button_text
=
u
"
\u2709
"
# ✉
required_roles
=
dd
.
required
(
SiteAdmin
)
parameters
=
dict
(
email
=
models
.
EmailField
(
_
(
'e-mail address'
)),
verification_code
=
models
.
CharField
(
_
(
"Verification code"
),
max_length
=
50
))
subject
=
models
.
CharField
(
_
(
'Subject'
),
max_length
=
250
),
#body_text=dd.RichTextField(_('Body text'), format='html'),
)
# params_layout = dd.Panel("""email
# subject
# body_text""", window_size=(60, 15))
# params_layout = dd.Panel("""
# email
# subject
# welcome_email_body""", window_size=(60, 15))
params_layout
=
"""
email
subject
"""
def
action_param_defaults
(
self
,
ar
,
obj
,
**
kw
):
kw
=
super
(
SendWelcomeMail
,
self
).
action_param_defaults
(
ar
,
obj
,
**
kw
)
if
obj
is
not
None
:
kw
.
update
(
email
=
obj
.
email
)
kw
.
update
(
subject
=
_
(
"Welcome on {site}"
).
format
(
site
=
settings
.
SITE
.
title
or
settings
.
SITE
.
verbose_name
))
# template = rt.get_template('users/welcome_email.eml')
# context = dict(obj=obj, E=E, rt=rt)
# body = template.render(**context)
# body = E.fromstring(body)
# kw.update(body_text=body)
return
kw
def
get_action_permission
(
self
,
ar
,
obj
,
state
):
if
obj
==
ar
.
get_user
():
user
=
ar
.
get_user
()
if
not
obj
.
email
:
return
False
if
not
user
.
profile
.
has_required_roles
([
SiteAdmin
]):
return
False
return
super
(
SendWelcomeMail
,
self
).
get_action_permission
(
ar
,
obj
,
state
)
def
run_from_ui
(
self
,
ar
,
**
kw
):
sender
=
settings
.
SERVER_EMAIL
done_for
=
[]
for
obj
in
ar
.
selected_rows
:
obj
.
send_welcome_email
()
done_for
.
append
(
str
(
obj
))
assert
len
(
ar
.
selected_rows
)
==
1
obj
=
ar
.
selected_rows
[
0
]
subject
=
ar
.
action_param_values
.
subject
email
=
ar
.
action_param_values
.
email
# body = ar.action_param_values.body_text
# body = "<body>" + body + "</body>"
email
=
"{} <{}>"
.
format
(
obj
,
email
)
body
=
obj
.
get_welcome_email_body
(
ar
)
print
(
20170102
,
obj
,
email
,
body
)
# send_welcome_email(obj)
rt
.
send_email
(
subject
,
sender
,
body
,
[
email
])
done_for
.
append
(
email
)
msg
=
_
(
"Welcome mail has been sent to {}."
).
format
(
', '
.
join
(
done_for
))
...
...
lino/modlib/users/choicelists.py
View file @
d243decf
...
...
@@ -11,7 +11,7 @@ from builtins import str
from
django.conf
import
settings
from
lino.core.choicelists
import
ChoiceList
,
Choice
from
lino.core.roles
import
UserRole
,
SiteUser
,
SiteAdmin
from
lino.core.roles
import
SiteAdmin
from
lino.api
import
dd
,
_
...
...
@@ -19,28 +19,31 @@ from lino.api import dd, _
class
UserType
(
Choice
):
"""Base class for all user profiles.
"""
.. attribute:: role
hidden_languages
=
None
"""A subset of :attr:`languages<lino.core.site.Site.languages>` which
should be hidden in this user profile. Default value is
:attr:`hidden_languages<UserTypes.hidden_languages>`. This is
used on multilingual sites with more than 4 or 5 languages.
The role of users having this type. This is an instance of
:class:`<lino.core.roles.UserRole>` or some subclass thereof.
.. attribute:: readonly
Whether users of this type get only write-proteced access.
.. attribute:: hidden_languages
A subset of :attr:`languages<lino.core.site.Site.languages>`
which should be hidden for users of this type. Default value
is :attr:`hidden_languages<UserTypes.hidden_languages>`. This
is used on multilingual sites with more than 4 or 5 languages.
"""
role
=
None
hidden_languages
=
None
readonly
=
False
"""Whether users with this profile get only write-proteced access."""
# authenticated = True
# """Whether users with this profile should be considered authenticated."""
role
=
None
"""The role of users having this profile. This is an instance of
:class:`<lino.core.roles.UserRole>` or some subclass thereof.
"""
def
__init__
(
self
,
value
,
text
,
role_class
,
name
=
None
,
# authenticated=True,
readonly
=
False
,
...
...
@@ -51,7 +54,6 @@ class UserType(Choice):
super
(
UserType
,
self
).
__init__
(
value
,
text
,
name
)
self
.
role
=
role_class
()
self
.
readonly
=
readonly
# self.authenticated = authenticated
self
.
kw
=
kw
def
attach
(
self
,
cls
):
...
...
@@ -104,6 +106,14 @@ class UserTypes(ChoiceList):
You can see the user profiles available in your application via
:menuselection:`Explorer --> System --> User Profiles`.
Every site should define at least three named user types:
.. attribute:: anonymous
.. attribute:: user
.. attribute:: admin
"""
required_roles
=
dd
.
login_required
(
SiteAdmin
)
item_class
=
UserType
...
...
@@ -124,11 +134,10 @@ class UserTypes(ChoiceList):
"""
add
=
UserTypes
.
add_item
add
(
'000'
,
_
(
"Anonymous"
),
UserRole
,
name
=
'anonymous'
,
readonly
=
True
)
add
(
'100'
,
_
(
"User"
),
SiteUser
,
name
=
'user'
)
add
(
'900'
,
_
(
"Administrator"
),
SiteAdmin
,
name
=
'admin'
)
# add = UserTypes.add_item
# add('000', _("Anonymous"), UserRole, 'anonymous', readonly=True)
# add('100', _("User"), SiteUser, 'user')
# add('900', _("Administrator"), SiteAdmin, 'admin')
lino/modlib/users/config/users/welcome_email.eml
View file @
d243decf
...
...
@@ -2,7 +2,9 @@
<p>Dear {{obj.get_full_name()}},</p>
<p>Welcome to the
<a href="{{rt.settings.SITE.server_url}}">{{rt.settings.SITE.server_url}}</a>
<a href="{{rt.settings.SITE.server_url}}">{{rt.settings.SITE.server_url}}</a>
site.
</p>
</body>
Please click {{E.tostring(ar.instance_action_button(obj.verify))}} and enter your verificataion code
:
<b>{{obj.verification_code}}</b>.
</body>
\ No newline at end of file
lino/modlib/users/desktop.py
View file @
d243decf
...
...
@@ -10,7 +10,7 @@ from lino.core import actions
from
lino.core.roles
import
SiteAdmin
from
.choicelists
import
UserTypes
from
.actions
import
SendWelcomeMail
class
UserDetail
(
dd
.
DetailLayout
):
...
...
@@ -71,9 +71,11 @@ class Users(dd.Table):
#~ return False
class
AllUsers
(
Users
):
"""Shows the list of all users on this site."""
required_roles
=
dd
.
required
(
SiteAdmin
)
send_welcome_email
=
SendWelcomeMail
()
class
UsersOverview
(
Users
):
...
...
lino/modlib/users/models.py
View file @
d243decf
...
...
@@ -11,11 +11,10 @@ from builtins import object
from
django.utils.encoding
import
python_2_unicode_compatible
from
django.db
import
models
from
django.utils.translation
import
ugettext_lazy
as
_
from
django.conf
import
settings
from
django.contrib.auth.base_user
import
AbstractBaseUser
from
lino.api
import
dd
,
rt
from
lino.api
import
dd
,
rt
,
_
from
lino.utils.xmlgen.html
import
E
from
lino.core
import
userprefs
from
lino.core.fields
import
NullCharField
...
...
@@ -111,6 +110,8 @@ class User(AbstractBaseUser, CreatedModified, TimezoneHolder):