Projet

Général

Profil

0001-general-serve-all-static-files-from-static-11582.patch

Frédéric Péters, 29 juin 2016 11:27

Télécharger (15,8 ko)

Voir les différences:

Subject: [PATCH] general: serve all static files from /static/ (#11582)

 INSTALL                                 |  7 ++-
 data/themes/alto/wcs.css                |  2 +-
 data/themes/default/template.mobile.ezt |  6 +-
 data/web/css/wcs.css                    |  2 +-
 data/web/index.html                     | 18 ------
 debian/vhost-apache-wcs                 | 12 +++-
 tests/test_ctl.py                       |  3 +
 tests/test_rootdirectory.py             | 10 ++--
 wcs/ctl/collectstatic.py                |  3 +-
 wcs/qommon/http_response.py             | 17 ++----
 wcs/qommon/publisher.py                 |  2 +-
 wcs/qommon/template.py                  |  2 +-
 wcs/root.py                             | 97 ++++++++++++++++-----------------
 13 files changed, 84 insertions(+), 97 deletions(-)
 delete mode 100644 data/web/index.html
INSTALL
33 33
      ServerAdmin webmaster@example.com
34 34
      ServerName www.example.com
35 35
      DocumentRoot /usr/share/wcs/web/
36
      Alias /qo/ /usr/share/wcs/qommon/
37 36

  
38 37
      SCGIMount / 127.0.0.1:3001
39
      <LocationMatch "^/(css|images|js|qo)/.*">
38

  
39
      # this part allows serving static files directly from Apache, but it
40
      # requires to run wcsctl.py collectstatic first.
41
      Alias /static/ /var/lib/wcs/collectstatic/
42
      <LocationMatch "^/static">
40 43
          SCGIHandler off
41 44
      </LocationMatch>
42 45

  
data/themes/alto/wcs.css
1 1
/* adapted from alto dotclear theme */
2 2

  
3
@import url(/qo/css/qommon.css);
3
@import url(/static/css/qommon.css);
4 4
 
5 5
html, body {
6 6
	background: #CCCCCC;
data/themes/default/template.mobile.ezt
3 3
  <head>
4 4
    <title>[page_title]</title>
5 5
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no, user-scalable=0">
6
    <script type="text/javascript" src="[root_url]qo/js/jquery.js"></script>
6
    <script type="text/javascript" src="[root_url]static/xstatic/jquery.js"></script>
7 7
    [script]
8
    <script type="text/javascript" src="[root_url]qo/js/wcs.mobile.js"></script>
9
    <link rel="stylesheet" type="text/css" href="[root_url]qo/css/mobile.css"/>
8
    <script type="text/javascript" src="[root_url]static/js/wcs.mobile.js"></script>
9
    <link rel="stylesheet" type="text/css" href="[root_url]static/css/mobile.css"/>
10 10
    <link rel="stylesheet" type="text/css" href="[theme_url]/mobile.css"/>
11 11
  </head>
12 12
  <body[if-any onload] onload="[onload]"[end]>
data/web/css/wcs.css
1
@import url(../qo/css/sofresh.css);
1
@import url(sofresh.css);
2 2

  
data/web/index.html
1
<html xmlns="http://www.w3.org/1999/xhtml">
2
  <head>
3
    <title>w.c.s.</title>
4
    <link rel="stylesheet" type="text/css" href="/css/wcs.css"/>
5
    <style type="text/css">
6
p#wcs {
7
	margin-top: 30%;
8
	text-align: center;
9
	font-weight: bold;
10
}
11
</style>
12
  </head>
13
  <body>
14
   <p id="wcs">
15
     w.c.s
16
   </p>
17
  </body>
18
</html>
debian/vhost-apache-wcs
2 2
    ServerAdmin webmaster@locahost
3 3
    ServerName www.example.com
4 4
    DocumentRoot /usr/share/wcs/web/
5
    Alias /qo/ /usr/share/wcs/qommon/
6 5

  
7 6
    SCGIMount / 127.0.0.1:3001
8
    <LocationMatch "^/(css|images|js|qo)/.*">
7

  
8
    Alias /static/ /var/lib/wcs/collectstatic/
9
    <LocationMatch "^/static">
9 10
        SCGIHandler off
10 11
    </LocationMatch>
11 12

  
13
    Alias /themes/ /usr/share/wcs/themes/
14
    RewriteEngine On
15
    RewriteCond  /usr/share/wcs/%{REQUEST_URI}  !-f
16
    RewriteCond  /usr/share/wcs/%{REQUEST_URI}  !-d
17
    RewriteRule  ^/themes/(.*)$ /var/lib/wcs/%{HTTP_HOST}/themes/$1
18

  
12 19
    CustomLog /var/log/apache2/wcs-access.log combined
13 20
    ErrorLog /var/log/apache2/wcs-error.log
14

  
15 21
</VirtualHost>
tests/test_ctl.py
21 21
def test_collectstatic(pub):
22 22
    CmdCollectStatic.collectstatic(pub)
23 23
    assert os.path.exists(os.path.join(pub.app_dir, 'collectstatic', 'css', 'wcs.css'))
24
    assert os.path.exists(os.path.join(pub.app_dir, 'collectstatic', 'css', 'qommon.css'))
25
    assert os.path.exists(os.path.join(pub.app_dir, 'collectstatic', 'css', 'gadjo.css'))
26
    assert os.path.exists(os.path.join(pub.app_dir, 'collectstatic', 'xstatic', 'jquery.js'))
24 27
    CmdCollectStatic.collectstatic(pub, clear=True, link=True)
25 28
    assert os.path.islink(os.path.join(pub.app_dir, 'collectstatic', 'css', 'wcs.css'))
tests/test_rootdirectory.py
150 150
    assert 'authentication required' in output  # locales ?
151 151

  
152 152
def test_static_directories():
153
    assert get_app(pub).get('/css/wcs.css')
154
    assert get_app(pub).get('/images/feed-icon-10x10.png')
155
    assert get_app(pub).get('/qo/css/qommon.css')
153
    assert get_app(pub).get('/static/css/wcs.css')
154
    assert get_app(pub).get('/static/images/feed-icon-10x10.png')
155
    assert get_app(pub).get('/static/css/qommon.css')
156 156
    if os.path.exists('/usr/share/javascript/leaflet/'):
157
        assert get_app(pub).get('/leaflet/leaflet.js')
157
        assert get_app(pub).get('/static/leaflet/leaflet.js')
158 158
    assert get_app(pub).get('/static/css/gadjo.css')
159 159
    assert get_app(pub).get('/static/xstatic/jquery.js')
160 160
    assert get_app(pub).get('/static/xstatic/jquery-ui.js')
161 161

  
162
    assert 'Directory listing denied' in get_app(pub).get('/css/').body
162
    assert 'Directory listing denied' in get_app(pub).get('/static/css/').body
163 163
    assert get_app(pub).get('/static/xxx', status=404)
wcs/ctl/collectstatic.py
41 41

  
42 42
    @classmethod
43 43
    def collectstatic(cls, pub, clear=False, link=False):
44
        root_directory_class = pub.root_directory_class
44
        from wcs.root import StaticsDirectory
45
        root_directory_class = StaticsDirectory
45 46
        static_dir = os.path.join(pub.app_dir, 'collectstatic')
46 47
        if clear and os.path.exists(static_dir):
47 48
            shutil.rmtree(static_dir)
wcs/qommon/http_response.py
60 60
        if not self.javascript_scripts:
61 61
            self.javascript_scripts = []
62 62
        mappings = {
63
            'jquery.js': '../../static/xstatic/jquery.js',
64
            'jquery-ui.js': '../../static/xstatic/jquery-ui.js',
63
            'jquery.js': '../xstatic/jquery.js',
64
            'jquery-ui.js': '../xstatic/jquery-ui.js',
65 65
        }
66 66
        for script_name in script_names:
67 67
            mapped_script_name = mappings.get(script_name) or script_name
68 68
            if not mapped_script_name in self.javascript_scripts:
69 69
                if script_name == 'qommon.map.js':
70 70
                    self.add_javascript(['jquery.js'])
71
                    self.add_javascript(['../../leaflet/leaflet.js'])
72
                    self.add_css_include('../../leaflet/leaflet.css')
71
                    self.add_javascript(['../leaflet/leaflet.js'])
72
                    self.add_css_include('../leaflet/leaflet.css')
73 73
                if script_name == 'jquery-ui.js':
74
                    self.add_css_include('../../static/xstatic/themes/smoothness/jquery-ui.min.css')
74
                    self.add_css_include('../xstatic/themes/smoothness/jquery-ui.min.css')
75 75
                self.javascript_scripts.append(str(mapped_script_name))
76 76
                if script_name == 'afterjob.js':
77 77
                    self.add_javascript_code('var QOMMON_ROOT_URL = "%s";\n' % \
......
92 92
        if self.javascript_scripts:
93 93
            root_url = get_publisher().get_root_url() + get_publisher().qommon_static_dir
94 94
            s += '\n'.join(['<script type="text/javascript" src="%sjs/%s"></script>' % (
95
                        root_url, str(x)) for x in self.javascript_scripts
96
                        if not x[0] == '/'])
97
            s += '\n'
98
            s += '\n'.join(['<script type="text/javascript" src="%sjs/%s"></script>' % (
99
                        get_publisher().get_root_url(), x[1:]) for x in self.javascript_scripts
100
                        if x[0] == '/'])
95
                        root_url, str(x)) for x in self.javascript_scripts])
101 96
            s += '\n'
102 97
        if self.javascript_code_parts:
103 98
            s += '<script type="text/javascript">'
wcs/qommon/publisher.py
81 81
    unpickler_class = None
82 82

  
83 83
    after_login_url = ''
84
    qommon_static_dir = 'qo/'
84
    qommon_static_dir = 'static/'
85 85
    qommon_admin_css = 'css/dc2/admin.css'
86 86
    default_theme = 'default'
87 87

  
wcs/qommon/template.py
363 363
        app_label = get_publisher().get_site_option('app_label') or 'w.c.s.'
364 364
    else:
365 365
        if current_theme == 'default':
366
            css = root_url + 'css/%s.css' % get_publisher().APP_NAME
366
            css = root_url + 'static/css/%s.css' % get_publisher().APP_NAME
367 367
        else:
368 368
            css = root_url + 'themes/%s/%s.css' % (current_theme, get_publisher().APP_NAME)
369 369

  
wcs/root.py
196 196
            return errors.TraversalError()
197 197

  
198 198

  
199
class StaticsDirectory(Directory):
200
    static_directories = {
201
        # maps /leaflet/ to the directory provided by the libjs-openlayers package
202
        'leaflet': ['/usr/share/javascript/leaflet'],
203
        '': ['web', 'qommon', 'django:gadjo'],
204
        'xstatic': ['xstatic:jquery', 'xstatic:jquery_ui', 'xstatic:font_awesome'],
205
    }
206

  
207
    @classmethod
208
    def resolve_static_directories(cls, prefix):
209
        directories = cls.static_directories[prefix]
210
        for directory in directories:
211
            if directory[0] == '/':
212
                yield directory
213
            elif not ':' in directory:
214
                yield os.path.join(get_publisher().data_dir, directory)
215
            else:
216
                directory_type, value = directory.split(':')
217
                try:
218
                    if directory_type == 'xstatic':
219
                        module = import_module('xstatic.pkg.%s' % value)
220
                        yield module.BASE_DIR
221
                    elif directory_type == 'django':
222
                        module = import_module(value)
223
                        yield os.path.join(os.path.dirname(module.__file__), 'static')
224
                except ImportError:
225
                    pass
226

  
227
    def _q_traverse(self, path):
228
        if path[0] in self.static_directories.keys():
229
            prefix, rest = path[0], path[1:]
230
        else:
231
            prefix, rest = '', path
232

  
233
        if not rest:
234
            raise errors.AccessForbiddenError()
235

  
236
        for directory in self.resolve_static_directories(prefix):
237
            try:
238
                return StaticDirectory(directory, follow_symlinks=True)._q_traverse(rest)
239
            except errors.TraversalError:
240
                continue
241
        raise errors.TraversalError()
242

  
243

  
199 244
class RootDirectory(Directory):
200 245
    _q_exports = ['admin', 'backoffice', 'forms', 'login', 'logout', 'saml',
201 246
            'ident', 'register', 'afterjobs', 'themes', 'myspace', 'user', 'roles',
202 247
            'pages', ('tmp-upload', 'tmp_upload'), 'api', '__version__',
203 248
            'tryauth', 'auth', 'preview', ('reload-top', 'reload_top'),
204
            'fargo', ('i18n.js', 'i18n_js')]
249
            'fargo', ('i18n.js', 'i18n_js'), 'static']
205 250

  
206 251
    api = ApiDirectory()
207 252
    themes = template.ThemesDirectory()
208 253
    myspace = MyspaceDirectory()
209 254
    pages = qommon.pages.PagesDirectory()
210 255
    fargo = file_validation.FargoDirectory()
211

  
212
    static_directories = {
213
        'css': ['web/css'],
214
        'images': ['web/images'],
215
        'qo': ['qommon'],
216
        # maps /leaflet/ to the directory provided by the libjs-openlayers package
217
        'leaflet': ['/usr/share/javascript/leaflet'],
218
        'static': ['django:gadjo'],
219
        'static_xstatic': ['xstatic:jquery', 'xstatic:jquery_ui', 'xstatic:font_awesome'],
220
    }
256
    static = StaticsDirectory()
221 257

  
222 258
    def tryauth(self):
223 259
        return forms.root.tryauth(get_publisher().get_root_url())
......
303 339
        if not self.backoffice:
304 340
            self.backoffice = get_publisher().backoffice_directory_class()
305 341

  
306
        if path and path[0] in self.static_directories:
307
            return self.serve_statics(path)
308

  
309 342
        try:
310 343
            return Directory._q_traverse(self, path)
311 344
        except errors.TraversalError:
......
313 346

  
314 347
        return forms.root.RootDirectory()._q_traverse(path)
315 348

  
316
    @classmethod
317
    def resolve_static_directories(cls, prefix):
318
        directories = cls.static_directories[prefix]
319
        for directory in directories:
320
            if directory[0] == '/':
321
                yield directory
322
            elif not ':' in directory:
323
                yield os.path.join(get_publisher().data_dir, directory)
324
            else:
325
                directory_type, value = directory.split(':')
326
                try:
327
                    if directory_type == 'xstatic':
328
                        module = import_module('xstatic.pkg.%s' % value)
329
                        yield module.BASE_DIR
330
                    elif directory_type == 'django':
331
                        module = import_module(value)
332
                        yield os.path.join(os.path.dirname(module.__file__), 'static')
333
                except ImportError:
334
                    pass
335

  
336
    def serve_statics(self, path):
337
        if path[:2] == ['static', 'xstatic']:
338
            # hack path so it's easier to lookup files from xstatic modules
339
            # (the gadjo xstatic storage adds a "xstatic" prefix that is not on
340
            # the filesystem, while StaticDirectory expects the filesystem
341
            # layout to match exactly; so we merge the first two elements in a
342
            # single one, so we get the real path in path[1:])
343
            path = ['static_xstatic'] + path[2:]
344

  
345
        for directory in self.resolve_static_directories(path[0]):
346
            try:
347
                return StaticDirectory(directory, follow_symlinks=True)._q_traverse(path[1:])
348
            except errors.TraversalError:
349
                continue
350
        raise errors.TraversalError()
351

  
352 349
    def _q_lookup(self, component):
353 350
        # is this a category ?
354 351
        try:
355
-