Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

# Copyright 2011-2015 Luc Saffre 

# License: BSD (see file COPYING for details) 

 

"""Model mixins for :mod:`lino.modlib.users`. 

 

.. autosummary:: 

 

""" 

 

from __future__ import unicode_literals 

from builtins import object 

 

import logging 

logger = logging.getLogger(__name__) 

 

from django.db import models 

 

from django.utils.translation import ugettext_lazy as _ 

from django.conf import settings 

 

from lino.api import dd 

 

from lino.core.exceptions import ChangedAPI 

from lino.core import model 

from lino.core import actions 

from lino.core import dbtables 

from lino.core.roles import SiteUser, SiteStaff, login_required 

 

from .utils import AnonymousUser 

 

 

class TimezoneHolder(models.Model): 

    """Mixin for database models which have a :attr:`timezone` field. 

 

    .. attribute:: timezone 

     

        The timezone. 

 

    """ 

    class Meta(object): 

        abstract = True 

 

    if settings.USE_TZ: 

        timezone = models.CharField(_("Time zone"), max_length=15, blank=True) 

    else: 

        timezone = dd.DummyField() 

 

    @dd.chooser(simple_values=True) 

    def timezone_choices(cls, partner): 

        import pytz 

        if partner and partner.country: 

            return pytz.country_timezones[partner.country.isocode] 

        return pytz.common_timezones 

 

 

class UserAuthored(model.Model): 

    """Model mixin for database objects that have a `user` field which 

    points to the "author" of this object. The default user is 

    automatically set to the requesting user. 

 

    .. attribute:: user 

 

        The author of this object. 

        A pointer to :class:`lino.modlib.users.models.User`. 

 

    """ 

 

    class Meta(object): 

        abstract = True 

 

    manager_roles_required = login_required(SiteStaff) 

    """The list of required roles for getting permission to edit other 

    users' work. 

 

    By default, only :class:`SiteStaff <lino.core.roles.SiteStaff>` 

    users can edit other users' work. 

 

    An application can set :attr:`manager_roles_required` to some 

    other user role class or a tuple of such classes. 

 

    Setting :attr:`manager_roles_required` to ``[]`` will **disable** 

    this behaviour (i.e. everybody can edit the work of other users). 

 

    This is going to be passed to :meth:`has_required_roles 

    <lino.core.users.choicelists.UserProfile.has_required_roles>` of 

    the requesting user's profile. 

 

    Usage examples see :class:`lino_xl.lib.notes.models.Note` or 

    :class:`lino_xl.lib.cal.models.Component`. 

 

    """ 

 

    workflow_owner_field = 'user' 

    user = dd.ForeignKey( 

        'users.User', 

        verbose_name=_("Author"), 

        related_name="%(app_label)s_%(class)s_set_by_user", 

        blank=True, null=True) 

 

    def on_create(self, ar): 

        """ 

        Adds the requesting user to the `user` field. 

 

        When acting as another user, the default implementation 

        still inserts the real user, not subst_user. 

        This is important for cal.Event. 

        """ 

        if self.user_id is None: 

            u = ar.user 

            if u is not None: 

                self.user = u 

        super(UserAuthored, self).on_create(ar) 

 

    def get_timezone(self): 

        """Return the author's timezone. Used by 

        :class:`lino_xl.lib.cal.mixins.Started`. 

 

        """ 

        if self.user_id is None: 

            return settings.TIME_ZONE 

        return self.user.timezone or settings.TIME_ZONE 

 

    def on_duplicate(self, ar, master): 

        """The default behaviour after duplicating is to change the author to 

        the user who requested the duplicate. 

 

        """ 

        if ar.user is not None: 

            self.user = ar.user 

        super(UserAuthored, self).on_duplicate(ar, master) 

 

    def get_row_permission(self, ar, state, ba): 

        """Only "managers" or "editors" can edit other users' work. 

 

        See also :attr:`manager_roles_required`. 

 

        """ 

        if not super(UserAuthored, self).get_row_permission(ar, state, ba): 

            return False 

        user = ar.get_user() 

        if self.user != ar.user \ 

           and (ar.subst_user is None or self.user != ar.subst_user) \ 

           and not user.profile.has_required_roles( 

               self.manager_roles_required): 

            return ba.action.readonly 

        return True 

 

    @classmethod 

    def on_analyze(cls, site): 

        if hasattr(cls, 'manager_level_field'): 

            raise ChangedAPI("{0} has a manager_level_field".format(cls)) 

        super(UserAuthored, cls).on_analyze(site) 

 

    @classmethod 

    def get_parameter_fields(cls, **fields): 

        """Adds the :attr:`user` filter parameter field.""" 

        fields.setdefault( 

            'user', models.ForeignKey( 

                'users.User', verbose_name=_("Author"), 

                blank=True, null=True)) 

        return super(UserAuthored, cls).get_parameter_fields(**fields) 

 

    @classmethod 

    def get_simple_parameters(cls): 

        s = super(UserAuthored, cls).get_simple_parameters() 

        s.add('user') 

        return s 

 

AutoUser = UserAuthored  # old name for backwards compatibility 

 

 

class My(dbtables.Table): 

    """Table mixin for tables on :class:`UserAuthored`. 

 

    Used by 

    :mod:`lino_xl.lib.excerpts` and 

    :mod:`lino_xl.lib.reception`. 

    """ 

 

    @classmethod 

    def get_actor_label(self): 

        if self.model is None: 

            return self._label or self.__name__ 

        return self._label or \ 

            _("My %s") % self.model._meta.verbose_name_plural 

 

    @classmethod 

    def param_defaults(self, ar, **kw): 

        kw = super(My, self).param_defaults(ar, **kw) 

        kw.update(user=ar.get_user()) 

        return kw 

 

 

class ByUser(dbtables.Table): 

    """Base table which fills the master instance from the web request. 

 

    """ 

    master_key = 'user' 

    #~ details_of_master_template = _("%(details)s of %(master)s") 

    details_of_master_template = _("%(details)s") 

 

    @classmethod 

    def get_actor_label(self): 

        if self.model is None: 

            return self._label or self.__name__ 

        return self._label or \ 

            _("My %s") % self.model._meta.verbose_name_plural 

 

    @classmethod 

    def setup_request(self, ar): 

        #~ logger.info("ByUser.setup_request") 

        if ar.master_instance is None: 

            u = ar.get_user() 

            if not isinstance(u, AnonymousUser): 

                ar.master_instance = u 

        super(ByUser, self).setup_request(ar) 

 

    @classmethod 

    def get_view_permission(self, profile): 

        if not profile.has_required_roles([SiteUser]): 

            return False 

        return super(ByUser, self).get_view_permission(profile) 

 

if settings.SITE.user_model is None: 

 

    # dummy Table for userless sites 

    ByUser = dbtables.Table 

 

 

class AuthorAction(actions.Action): 

    """ 

    """ 

    manager_roles_required = login_required(SiteStaff) 

 

    def get_action_permission(self, ar, obj, state): 

        user = ar.get_user() 

        if obj.user != user and \ 

           not user.profile.has_required_roles(self.manager_roles_required): 

            return self.readonly 

        return super( 

            AuthorAction, self).get_action_permission(ar, obj, state)