Skip to content

Commit

Permalink
implement meta-bundle generation and injection
Browse files Browse the repository at this point in the history
  • Loading branch information
ebrehault committed Feb 18, 2016
1 parent 223d8c7 commit 398cebb
Show file tree
Hide file tree
Showing 5 changed files with 190 additions and 24 deletions.
101 changes: 101 additions & 0 deletions Products/CMFPlone/resources/browser/combine.py
@@ -0,0 +1,101 @@
from Acquisition import aq_base
from datetime import datetime
from plone.registry.interfaces import IRegistry
from plone.resource.file import FilesystemFile
from plone.resource.interfaces import IResourceDirectory
from Products.CMFPlone.interfaces import IBundleRegistry
from Products.CMFPlone.interfaces.resources import (
OVERRIDE_RESOURCE_DIRECTORY_NAME,
)
from StringIO import StringIO
from zope.component import getUtility

PRODUCTION_RESOURCE_DIRECTORY = "production"


def get_production_resource_directory():
persistent_directory = getUtility(IResourceDirectory, name="persistent")
container = persistent_directory[OVERRIDE_RESOURCE_DIRECTORY_NAME]
production_folder = container[PRODUCTION_RESOURCE_DIRECTORY]
timestamp = production_folder.readFile('timestamp.txt')
return "%s/++unique++%s" % (
PRODUCTION_RESOURCE_DIRECTORY, timestamp)


def get_resource(context, path):
resource = context.unrestrictedTraverse(path)
if isinstance(resource, FilesystemFile):
(directory, sep, filename) = path.rpartition('/')
return context.unrestrictedTraverse(directory).readFile(filename)
else:
if hasattr(aq_base(resource), 'GET'):
# for FileResource
return resource.GET()
else:
# any BrowserView
return resource()


def write_js(context, folder, meta_bundle):
registry = getUtility(IRegistry)
resources = []

# default resources
if meta_bundle == 'default' and registry.records.get(
'plone.resources/jquery.js'
):
resources.append(get_resource(context,
registry.records['plone.resources/jquery.js'].value))
resources.append(get_resource(context,
registry.records['plone.resources.requirejs'].value))
resources.append(get_resource(context,
registry.records['plone.resources.configjs'].value))

# bundles
bundles = registry.collectionOfInterface(
IBundleRegistry, prefix="plone.bundles", check=False)
for bundle in bundles.values():
if bundle.merge_with == meta_bundle:
resources.append(get_resource(context, bundle.jscompilation))

fi = StringIO()
for script in resources:
fi.write(script + '\n')
folder.writeFile(meta_bundle + ".js", fi)


def write_css(context, folder, meta_bundle):
registry = getUtility(IRegistry)
resources = []

bundles = registry.collectionOfInterface(
IBundleRegistry, prefix="plone.bundles", check=False)
for bundle in bundles.values():
if bundle.merge_with == meta_bundle:
resources.append(get_resource(context, bundle.csscompilation))

fi = StringIO()
for script in resources:
fi.write(script + '\n')
folder.writeFile(meta_bundle + ".css", fi)


def combine_bundles(context):
persistent_directory = getUtility(IResourceDirectory, name="persistent")
if OVERRIDE_RESOURCE_DIRECTORY_NAME not in persistent_directory:
persistent_directory.makeDirectory(OVERRIDE_RESOURCE_DIRECTORY_NAME)
container = persistent_directory[OVERRIDE_RESOURCE_DIRECTORY_NAME]
if PRODUCTION_RESOURCE_DIRECTORY not in container:
container.makeDirectory(PRODUCTION_RESOURCE_DIRECTORY)
production_folder = container[PRODUCTION_RESOURCE_DIRECTORY]

# store timestamp
fi = StringIO()
fi.write(datetime.now().isoformat())
production_folder.writeFile("timestamp.txt", fi)

# generate new combined bundles
write_js(context, production_folder, 'default')
write_js(context, production_folder, 'logged-in')
write_css(context, production_folder, 'default')
write_css(context, production_folder, 'logged-in')
5 changes: 5 additions & 0 deletions Products/CMFPlone/resources/browser/cook.py
Expand Up @@ -3,6 +3,7 @@
from Products.CMFPlone.interfaces.resources import IResourceRegistry
from Products.CMFPlone.interfaces.resources import IBundleRegistry
from Products.CMFPlone.interfaces.resources import OVERRIDE_RESOURCE_DIRECTORY_NAME # noqa
from Products.CMFPlone.resources.browser.combine import combine_bundles
from StringIO import StringIO
from cssmin import cssmin
from datetime import datetime
Expand Down Expand Up @@ -127,5 +128,9 @@ def cookWhenChangingSettings(context, bundle=None):
# setRequest(original_request)
except NotFound:
logger.info('Error compiling js/css for the bundle')

# refresh production meta bundles
combine_bundles(context)

# Disable CSRF protection on this request
alsoProvides(request, IDisableCSRFProtection)
59 changes: 38 additions & 21 deletions Products/CMFPlone/resources/browser/resource.py
Expand Up @@ -15,11 +15,19 @@
from plone.memoize.view import memoize
from Products.CMFPlone.resources import RESOURCE_DEVELOPMENT_MODE

from .combine import get_production_resource_directory


class ResourceView(ViewletBase):
"""Information for script rendering.
"""

@property
@memoize
def anonymous(self):
return _getAuthenticatedUser(
self.context).getUserName() == 'Anonymous User'

@property
@memoize
def development(self):
Expand All @@ -32,7 +40,7 @@ def development(self):
"""
if RESOURCE_DEVELOPMENT_MODE:
return True
if _getAuthenticatedUser(self.context).getUserName() == 'Anonymous User':
if self.anonymous:
return False
return self.registry.records['plone.resources.development'].value

Expand Down Expand Up @@ -83,6 +91,25 @@ def update(self):
self.site_url = self.portal_state.portal_url()
self.registry = getUtility(IRegistry)

self.production_path = get_production_resource_directory()

self.diazo_production_css = None
self.diazo_development_css = None
self.diazo_development_js = None
self.diazo_production_js = None
self.themeObj = None

# Check if its Diazo enabled
policy = theming_policy(self.request)
if policy.isThemeEnabled():
self.themeObj = policy.get_theme()
if self.themeObj:
if hasattr(self.themeObj, 'production_css'):
self.diazo_production_css = self.themeObj.production_css
self.diazo_development_css = self.themeObj.development_css
self.diazo_development_js = self.themeObj.development_js
self.diazo_production_js = self.themeObj.production_js

def get_bundles(self):
return self.registry.collectionOfInterface(
IBundleRegistry, prefix="plone.bundles", check=False)
Expand All @@ -97,26 +124,13 @@ def get_cooked_bundles(self):
"""
cache = component.queryUtility(ram.IRAMCache)
bundles = self.get_bundles()
policy = theming_policy(self.request)

enabled_diazo_bundles = []
disabled_diazo_bundles = []
self.diazo_production_css = None
self.diazo_development_css = None
self.diazo_development_js = None
self.diazo_production_js = None

# Check if its Diazo enabled
if policy.isThemeEnabled():
themeObj = policy.get_theme()
if themeObj:
enabled_diazo_bundles = themeObj.enabled_bundles
disabled_diazo_bundles = themeObj.disabled_bundles
if hasattr(themeObj, 'production_css'):
self.diazo_production_css = themeObj.production_css
self.diazo_development_css = themeObj.development_css
self.diazo_development_js = themeObj.development_js
self.diazo_production_js = themeObj.production_js
if self.themeObj:
enabled_diazo_bundles = self.themeObj.enabled_bundles
disabled_diazo_bundles = self.themeObj.disabled_bundles

# Request set bundles
enabled_request_bundles = []
Expand Down Expand Up @@ -157,7 +171,7 @@ def get_cooked_bundles(self):
continue
yield key, bundle

def ordered_bundles_result(self):
def ordered_bundles_result(self, production=False):
"""
It gets the ordered result of bundles
"""
Expand All @@ -168,7 +182,8 @@ def ordered_bundles_result(self):
for key, bundle in self.get_cooked_bundles():
if bundle.depends is None or bundle.depends == '':
# its the first one
self.get_data(bundle, result)
if not(production and bundle.merge_with):
self.get_data(bundle, result)
inserted.append(key)
else:
name = bundle.depends.strip()
Expand All @@ -184,7 +199,8 @@ def ordered_bundles_result(self):
if key in inserted:
found = True
for bundle in bundles_to_add:
self.get_data(bundle, result)
if not(production and bundle.merge_with):
self.get_data(bundle, result)
inserted.append(
bundle.__prefix__.split('/', 1)[1].rstrip('.'))
del depends_on[key]
Expand All @@ -194,6 +210,7 @@ def ordered_bundles_result(self):
# THe ones that does not get the dependencies
for bundles_to_add in depends_on.values():
for bundle in bundles_to_add:
self.get_data(bundle, result)
if not(production and bundle.merge_with):
self.get_data(bundle, result)

return result
24 changes: 22 additions & 2 deletions Products/CMFPlone/resources/browser/scripts.py
Expand Up @@ -111,8 +111,28 @@ def scripts(self):
"""The requirejs scripts, the ones that are not resources are loaded on
configjs.py
"""
result = self.default_resources()
result.extend(self.ordered_bundles_result())
if self.development:
result = self.default_resources()
result.extend(self.ordered_bundles_result())
else:
result = [{
'src': '%s/++plone++%s' % (
self.site_url,
self.production_path + '/default.js'
),
'conditionalcomment': None,
'bundle': 'production'
}, ]
if not self.anonymous:
result.append({
'src': '%s/++plone++%s' % (
self.site_url,
self.production_path + '/logged-in.js'
),
'conditionalcomment': None,
'bundle': 'production'
})
result.extend(self.ordered_bundles_result(production=True))

# Add manual added resources
if hasattr(self.request, 'enabled_resources'):
Expand Down
25 changes: 24 additions & 1 deletion Products/CMFPlone/resources/browser/styles.py
Expand Up @@ -89,7 +89,30 @@ def styles(self):
"""
Get all the styles
"""
result = self.ordered_bundles_result()
if self.development:
result = self.ordered_bundles_result()
else:
result = [{
'src': '%s/++plone++%s' % (
self.site_url,
self.production_path + '/default.css'
),
'conditionalcomment': None,
'rel': 'stylesheet',
'bundle': 'production'
}, ]
if not self.anonymous:
result.append({
'src': '%s/++plone++%s' % (
self.site_url,
self.production_path + '/logged-in.css'
),
'conditionalcomment': None,
'rel': 'stylesheet',
'bundle': 'production'
})
result.extend(self.ordered_bundles_result(production=True))

# Add manual added resources
resources = self.get_resources()
if hasattr(self.request, 'enabled_resources'):
Expand Down

0 comments on commit 398cebb

Please sign in to comment.