finders.py 3.69 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
"""
Static file finders for Django.
https://docs.djangoproject.com/en/1.8/ref/settings/#std:setting-STATICFILES_FINDERS
Yes, this interface is private and undocumented, but we need to access it anyway.

In order to deploy Open edX in production, it's important to be able to collect
and process static assets: images, CSS, JS, fonts, etc. Django's collectstatic
system is the accepted way to do that in Django-based projects, but that system
doesn't handle every kind of collection and processing that web developers need.
Other open source projects like `Django-Pipeline`_ and `Django-Require`_ hook
into Django's collectstatic system to provide features like minification,
compression, Sass pre-processing, and require.js optimization for assets before
they are pushed to production. To make sure that themed assets are collected
and served by the system (in addition to core assets), we need to extend this
interface, as well.

.. _Django-Pipeline: http://django-pipeline.readthedocs.org/
.. _Django-Require: https://github.com/etianen/django-require
"""
20 21 22
import os
from collections import OrderedDict

23 24
from django.contrib.staticfiles import utils
from django.contrib.staticfiles.finders import BaseFinder
25 26 27 28
from django.utils import six

from openedx.core.djangoapps.theming.helpers import get_themes
from openedx.core.djangoapps.theming.storage import ThemeStorage
29 30


31
class ThemeFilesFinder(BaseFinder):
32
    """
33 34
    A static files finder that looks in the directory of each theme as
    specified in the source_dir attribute.
35
    """
36 37 38
    storage_class = ThemeStorage
    source_dir = 'static'

39
    def __init__(self, *args, **kwargs):
40 41 42 43
        # The list of themes that are handled
        self.themes = []
        # Mapping of theme names to storage instances
        self.storages = OrderedDict()
44

45 46 47 48 49 50
        themes = get_themes()
        for theme in themes:
            theme_storage = self.storage_class(
                os.path.join(theme.path, self.source_dir),
                prefix=theme.theme_dir_name,
            )
51

52 53 54
            self.storages[theme.theme_dir_name] = theme_storage
            if theme.theme_dir_name not in self.themes:
                self.themes.append(theme.theme_dir_name)
55

56
        super(ThemeFilesFinder, self).__init__(*args, **kwargs)
57

58 59 60 61 62 63 64 65
    def list(self, ignore_patterns):
        """
        List all files in all app storages.
        """
        for storage in six.itervalues(self.storages):
            if storage.exists(''):  # check if storage location exists
                for path in utils.get_files(storage, ignore_patterns):
                    yield path, storage
66 67 68

    def find(self, path, all=False):  # pylint: disable=redefined-builtin
        """
69
        Looks for files in the theme directories.
70
        """
71 72
        matches = []
        theme_dir_name = path.split("/", 1)[0]
73

74 75 76 77 78 79 80 81 82 83 84
        themes = {t.theme_dir_name: t for t in get_themes()}
        # if path is prefixed by theme name then search in the corresponding storage other wise search all storages.
        if theme_dir_name in themes:
            theme = themes[theme_dir_name]
            path = "/".join(path.split("/")[1:])
            match = self.find_in_theme(theme.theme_dir_name, path)
            if match:
                if not all:
                    return match
                matches.append(match)
        return matches
85

86
    def find_in_theme(self, theme, path):
87
        """
88
        Find a requested static file in an theme's static locations.
89
        """
90 91 92 93 94 95 96
        storage = self.storages.get(theme, None)
        if storage:
            # only try to find a file if the source dir actually exists
            if storage.exists(path):
                matched_path = storage.path(path)
                if matched_path:
                    return matched_path