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

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

# -*- coding: UTF-8 -*- 

# Copyright 2008-2015 Luc Saffre. 

# License: BSD, see LICENSE for more details. 

 

"""This defines the :class:`Plugin` class. 

See :doc:`/dev/plugins` before reading this. 

 

 

""" 

from future import standard_library 

standard_library.install_aliases() 

from builtins import object 

 

import os 

from os.path import exists, join, dirname, isdir, abspath 

 

from urllib.parse import urlencode 

import inspect 

 

 

class Plugin(object): 

    """The base class for all plugins. 

 

    For an introduction, see :doc:`/dev/plugins`. 

 

    A :class:`Plugin` is an optional descriptor for an app which gets 

    defined and configured before Django models start to load. 

    Lino creates one :class:`Plugin` instance for every installed app. 

 

    The :class:`Plugin` class is comparable to Django's `AppConfig 

    <https://docs.djangoproject.com/en/1.7/ref/applications/>`_ class 

    which has been added in version 1.7., but there is at least one 

    fundamental difference: in Lino the :class:`Plugin` instances for 

    all installed apps are available (in :attr:`dd.plugins 

    <lino.core.site.Site.plugins>`) when the :xfile:`settings.py` file 

    has been loaded and *before* Django starts to load the first 

    :xfile:`models.py`.  This is possible because Plugins are defined 

    in your app's :xfile:`__init__.py` file. 

 

    Unlike Django's `AppConfig`, you *cannot* define a `Plugin` in 

    your :xfile:`models.py` file, you *must* define it in your app's 

    :xfile:`__init__.py`.  This limitation has the advantage of making 

    certain things possible which are not possible in plain Django. 

 

    Plugins get instiantiated when the :class:`Site` object 

    instantiates (i.e. before Django settings are ready). 

 

    """ 

 

    verbose_name = None 

    """The verbose name of this plugin, as shown to the user.  This can be 

    a lazily translated string. 

 

    """ 

 

    short_name = None 

    """The abbreviated name of this plugin, shown to the user in places 

    where shortness is important, e.g. as the label of the tabs of a 

    detail layout.  This can be a lazily translated string. Defaults 

    to :attr:`verbose_name`. 

 

    """ 

 

    needs_plugins = [] 

    """A list of names of plugins on which this plugin depends. 

 

    Lino will automatically add these to your 

    `INSTALLED_APPS` if necessary. 

    Note that Lino will add them *after* your app. 

    To have them *before* your app, specify them explicitly. 

 

    """ 

 

    needed_by = None 

    """If not None, then it is the Plugin instance which caused this 

    plugin to automatically install. 

 

    """ 

 

    extends_models = None 

    """If specified, a list of model names for which this app provides a 

    subclass. 

 

    For backwards compatibility this has no effect 

    when :setting:`override_modlib_models` is set. 

 

    """ 

 

    ui_label = None 

 

    ui_handle_attr_name = None 

    """Currently implemented by :mod:`lino.modlib.extjs`, 

    :mod:`lino.modlib.bootstrap3`.""" 

 

    media_base_url = None 

    """ 

    Remote URL base for media files. 

 

    """ 

 

    media_root = None 

    """ 

    Local path where third-party media files are installed. 

 

    Only used if this app has :attr:`media_base_url` empty and 

    :attr:`media_name` non-empty, *and* if the :xfile:`media` 

    directory has no entry named :attr:`media_name`. 

 

    """ 

 

    media_name = None 

    """ 

    Either `None` (default) or a non-empty string with the name of the 

    subdirectory of your :xfile:`media` directory which is expected to 

    contain media files for this app. 

 

    `None` means that there this app has no media files of her own. 

 

    Best practice is to set this to the `app_label`.  Will be ignored 

    if :attr:`media_base_url` is nonempty. 

 

    """ 

 

    url_prefix = None 

    """ 

    The url prefix under which this app should ask to 

    install its url patterns. 

    """ 

 

    site_js_snippets = [] 

    """ 

    List of js snippets to be injected into the `lino_*.js` file. 

 

    """ 

 

    renderer = None 

    """The renderer used by this plugin. See :doc:`/dev/rendering`.""" 

 

    def __init__(self, site, app_label, app_name, app_module, needed_by): 

        """This is called when the Site object *instantiates*, i.e.  you may 

        not yet import `django.conf.settings`.  But you get the `site` 

        object being instantiated. 

 

        Parameters: 

 

        :site:       The :class:`Site` instance 

        :app_label:  e.g. "contacts" 

        :app_name:   e.g. "lino.modlib.contacts" 

        :app_module: The module object corresponding to the 

                     :xfile:`__init__.py` file. 

 

        """ 

        # site.logger.info("20140226 Plugin.__init__() %s", 

        #                  app_label) 

        if site._startup_done:  # djangotest.TestCase.__call__ 

            raise Exception(20140227) 

        self.site = site 

        self.app_name = app_name 

        self.app_label = app_label 

        self.app_module = app_module 

        self.needed_by = needed_by 

        if self.verbose_name is None: 

            self.verbose_name = app_label.title() 

        if self.short_name is None: 

            self.short_name = self.verbose_name 

        self.on_init() 

        # import pdb; pdb.set_trace() 

        # super(Plugin, self).__init__() 

 

    def configure(self, **kw): 

        """Set the given parameter(s) of this Plugin instance.  Any number of 

        parameters can be specified as keyword arguments. 

 

        Raise an exception if caller specified a key that does not 

        have a corresponding attribute. 

 

        """ 

        for k, v in list(kw.items()): 

            if not hasattr(self, k): 

                raise Exception("%s has no attribute %s" % (self, k)) 

            setattr(self, k, v) 

 

    def get_used_libs(self, html=None): 

        return [] 

 

    def on_init(self): 

        """This will be called when the Plugin is being instantiated (i.e. 

        even before the :class:`Site` instantiation has finished. Used by 

        :mod:`lino.modlib.users` to set :attr:`user_model`. 

 

        """ 

        pass 

 

    def on_site_startup(self, site): 

        """This will be called exactly once, when models are ready. 

 

        """ 

        pass 

 

    @classmethod 

    def extends_from(cls): 

        """Return the plugin from which this plugin inherits.""" 

        # for p in self.__class__.__bases__: 

        for p in cls.__bases__: 

            if issubclass(p, Plugin): 

                return p 

        # raise Exception("20140825 extends_from failed") 

 

    @classmethod 

    def get_subdir(cls, name): 

        """Get the absolute path of the named subdirectory if it exists.""" 

        p = dirname(inspect.getfile(cls)) 

        p = abspath(join(p, name)) 

        if isdir(p): 

            return p 

        # print("20150331 %s : no directory %s" % (cls, p)) 

 

    def before_analyze(self): 

        """This is called during startup, when all models modules have been 

        imported, and before Lino starts to analyze them. 

 

        """ 

        pass 

 

    def on_ui_init(self, kernel): 

        """This is called when the kernel is being instantiated. 

        """ 

        pass 

 

    def __repr__(self): 

        l = [] 

        for k in ('media_name', 'media_root', 'media_base_url', 

                  'extends_models', 'needed_by'): 

            v = getattr(self, k, None) 

            if v is not None: 

                l.append('%s=%s' % (k, v)) 

        if len(l) == 0: 

            return self.app_name 

        return "%s (%s)" % (self.app_name, ', '.join(l)) 

 

    def get_patterns(self): 

        """Return a list of url patterns to be added to the Site's patterns. 

 

        """ 

        return [] 

 

    def get_css_includes(self, site): 

        return [] 

 

    def get_js_includes(self, settings, language): 

        return [] 

 

    def get_head_lines(cls, site, request): 

        """Yield or return a list of textlines to add to the `<head>` of the 

        html page.""" 

        return [] 

 

    def get_body_lines(cls, site, request): 

        return [] 

 

    def get_row_edit_lines(self, e, panel): 

        return [] 

 

    def build_static_url(self, *parts, **kw): 

        raise Exception("Renamed to build_lib_url") 

 

    def build_lib_url(self, *parts, **kw): 

        if self.media_base_url: 

            url = self.media_base_url + '/'.join(parts) 

            if len(kw): 

                url += "?" + urlencode(kw) 

            return url 

        return self.site.build_static_url(self.media_name, *parts, **kw) 

 

    def build_plain_url(self, *args, **kw): 

        if self.url_prefix: 

            return self.site.buildurl(self.url_prefix, *args, **kw) 

        return self.site.buildurl(*args, **kw) 

 

    def get_menu_group(self): 

        """Return the plugin into whose menu this plugin wants to be inserted. 

        If this plugin was automatically installed because some other 

        plugin needs it, return that other plugin. Otherwise return 

        this plugin. 

 

        Used by :mod:`lino.modlib.languages`. 

        Returns a :class:`Plugin` instance. 

 

        """ 

        return self.needed_by or self