Using Buildout, GenericSetup and a Policy Package to Rule the World

Preview:

DESCRIPTION

So you have your code on the filesystem and you are using buildout;what's next? You can take your build to the next level by reducing theamount of manual steps needed to create and maintain your site. Usingcollective.recipe.plonesite, you can ensure that every member of yourteam is working on an identical Plone site at any given time.Without the plonesite recipe, when you run your buildout for the firsttime, you are left with an empty Zope site. This talk will show you howto utilize buildout to create a Plone site for you and make sure it isall set up via a policy package and GenericSetup so you can hit theground running.

Citation preview

Clayton Parker | Senior Developer

Using Buildout, GenericSetup and a Policy Package to Rule the World

PLONE SYMPOSIUM EAST 2012

Who Am I

PLONE SYMPOSIUM EAST 2012What will we learn?

• Policy Package

• GenericSetup

• Plone Site Buildout Recipe

http://github.com/sixfeetup

Demo

Realization• Repeatable environments

• Fewer commands

• Repeatable product installation

PLONE SYMPOSIUM EAST 2012

PLONE SYMPOSIUM EAST 2012Human Err0r

“Hey Bob, did you run the fizzbang.widget profile on production”

“I think so, Doug”

“Do you know why the client is yelling at me right now?”

“Maybe I didn’t, let me fix that real quick”

Policy Package

PLONE SYMPOSIUM EAST 2012Create the package*

$ cd path/to/buildout/src$ zopeskel sfu_policy pse12.policy$ cd pse12.policy.. git init / add / etc ...

* This example uses sixieskel for brevity, you might use the “plone” template

PLONE SYMPOSIUM EAST 2012Package layout!"" setup.py#"" src #"" pse12    !"" __init__.py    #"" policy    !"" __init__.py    !"" configure.zcml    !"" profiles    %   !"" default    %   %   #"" metadata.xml    %   #"" initial    %   #"" metadata.xml    !"" setuphandlers.py    !"" upgrades.py    #"" upgrades.zcml

PLONE SYMPOSIUM EAST 2012Add to buildout

[buildout]extensions = mr.developerauto-checkout = Trueparts = instance

[sources]pse12.policy = git <git url here>

[instance]eggs = pse12.policy

PLONE SYMPOSIUM EAST 2012Dependencies

# pse12.policy/setup.pyinstall_requires=[ 'setuptools', 'Plone', 'Pillow', 'plone.app.caching',],

PLONE SYMPOSIUM EAST 2012ZCML# pse12.policy setup.pyentry_points="""[z3c.autoinclude.plugin]target = plone""",

<!-- pse12.policy configure.zcml --><includePlugins package="." /><includeDependencies package="." />

Generic Setup

PLONE SYMPOSIUM EAST 2012Default profile

<!-- pse12.policy configure.zcml --><genericsetup:registerProfile name="default" title="pse12.policy (default)" directory="profiles/default" description="Installation profile for pse12.policy" provides="Products.GenericSetup.interfaces.EXTENSION" />

PLONE SYMPOSIUM EAST 2012Initial profile

<!-- pse12.policy configure.zcml --><genericsetup:registerProfile name="initial" title="pse12.policy (initial)" directory="profiles/initial" description="Initial profile for pse12.policy" provides="Products.GenericSetup.interfaces.EXTENSION" />

PLONE SYMPOSIUM EAST 2012Metadata

<?xml version="1.0"?><metadata> <version>001</version> <dependencies>...</dependencies></metadata>

Add-ons

PLONE SYMPOSIUM EAST 2012Package

# Inside setup.pyinstall_requires=[ ... ‘plonetheme.transition’,],

PLONE SYMPOSIUM EAST 2012Package

<?xml version="1.0"?>

<!-- pse12.policy metadata.xml --><metadata> <version>001</version> <dependencies> <dependency>profile-plone.app.theming:default</dependency> </dependencies></metadata>

PLONE SYMPOSIUM EAST 2012Package

PLONE SYMPOSIUM EAST 2012Package# profiles/default/registry.xml<registry> <record field="enabled" interface="plone.app.theming.interfaces.IThemeSettings" name="plone.app.theming.interfaces.IThemeSettings.enabled"> <field type="plone.registry.field.Bool"> <default>False</default> <description>enable_theme_globally</description> <title>enabled</title> </field> <value>True</value> </record> <record field="absolutePrefix" interface="plone.app.theming.interfaces.IThemeSettings" name="plone.app.theming.interfaces.IThemeSettings.absolutePrefix"> <field type="plone.registry.field.TextLine"> <description>convert_relative_url</description> <required>False</required> <title>absolute_url_prefix</title> </field> <value>/++theme++plonetheme.transition</value> </record> <record field="currentTheme" interface="plone.app.theming.interfaces.IThemeSettings" name="plone.app.theming.interfaces.IThemeSettings.currentTheme"> <field type="plone.registry.field.TextLine"> <description>current_theme_description</description> <title>current_theme</title> </field> <value>plonetheme.transition</value> </record> <record field="rules" interface="plone.app.theming.interfaces.IThemeSettings" name="plone.app.theming.interfaces.IThemeSettings.rules"> <field type="plone.registry.field.TextLine"> <description>rules_file_path</description> <required>False</required> <title>rules_file</title> </field> <value>/++theme++plonetheme.transition/rules.xml</value> </record></registry>

PLONE SYMPOSIUM EAST 2012Blog

# Inside pse12.policy setup.pyinstall_requires=[ ... ‘collective.blog.star’,],

PLONE SYMPOSIUM EAST 2012Blog

<?xml version="1.0"?>

<!-- pse12.policy metadata.xml --><metadata> <version>001</version> <dependencies> ... <dependency>profile-collective.blog.star:default</dependency> </dependencies></metadata>

Content

PLONE SYMPOSIUM EAST 2012Package

$ zopeskel plone pse12.initialcontent

PLONE SYMPOSIUM EAST 2012Package

# Inside pse12.initialcontent setup.pyinstall_requires=[ ‘quintagroup.transmogrifier’,],

PLONE SYMPOSIUM EAST 2012Package

<!-- pse12.initialcontent configure.zcml --><genericsetup:registerProfile name="default" title="pse12.initialcontent (default)" directory="profiles/default" description="Content generation package" provides="Products.GenericSetup.interfaces.EXTENSION" />

PLONE SYMPOSIUM EAST 2012Package

<?xml version="1.0"?>

<!-- pse12.initialcontent metadata.xml --><metadata> <version>001</version> <dependencies> <dependency>profile-pse12.policy:default</dependency> </dependencies></metadata>

PLONE SYMPOSIUM EAST 2012!"" __init__.py!"" configure.zcml!"" profiles#   %"" default#   !"" metadata.xml#   !"" pse12_initialcontent-default.txt#   !"" quintagroup.transmogrifier-import.txt#   %"" structure#   !"" .objects.xml#   !"" .portlets.xml#   !"" .properties.xml#   !"" about#   #   !"" .marshall.xml#   #   !"" .objects.xml#   #   !"" .portlets.xml#   #   !"" .properties.xml#   #   !"" about-us#   #   #   !"" .marshall.xml#   #   #   %"" .portlets.xml#   #   %"" meet-the-team#   #   !"" .marshall.xml#   #   %"" .portlets.xml#   !"" files#   #   !"" .marshall.xml#   #   %"" .portlets.xml#   !"" front-page#   #   !"" .marshall.xml#   #   %"" .portlets.xml#   %"" images#      !"" .marshall.xml#      !"" .objects.xml#     %"" .portlets.xml!"" setuphandlers.py!"" upgrades.py%"" upgrades.zcml

Upgrades and Setuphandlers

PLONE SYMPOSIUM EAST 2012Setuphandlersfrom sixfeetup.utils import helpers as sfutils

def importVariousInitial(context): """Run the setup handlers for the initial profile""" if context.readDataFile('pse12_policy-initial.txt') is None: return members = [ {'id': 'staff', 'password': 'staff', 'roles': ['Manager', 'Member'], 'properties': { 'email': 'changeme@example.com', 'fullname': 'Site Staff', 'username': 'staff' } } ] sfutils.addUserAccounts(members)

PLONE SYMPOSIUM EAST 2012Setuphandlers

<genericsetup:importStep name="pse12.policy: initial" title="pse12.policy: Various Initial steps" description="Initial Setup handlers for pse12.policy" handler="pse12.policy.setuphandlers.importVariousInitial"> <depends name="content"/> </genericsetup:importStep>

PLONE SYMPOSIUM EAST 2012Setuphandlers

from sixfeetup.utils import helpers as sfutils

def importVarious(context): """Run the setup handlers for the default profile""" if context.readDataFile('pse12_policy-default.txt') is None: return # automagically run a plone migration if needed sfutils.runPortalMigration() # automagically run the upgrade steps for this package sfutils.runUpgradeSteps(u'pse12.policy:default')

PLONE SYMPOSIUM EAST 2012Setuphandlers

from sixfeetup.utils import helpers as sfutils

def importVarious(context): """Run the setup handlers for the default profile""" if context.readDataFile('pse12_initialcontent-default.txt') is None: return # automagically run the upgrade steps for this package sfutils.runUpgradeSteps(u'pse12.initialcontent:default')

PLONE SYMPOSIUM EAST 2012Upgrades <!-- pse12.initialcontent upgrades.zcml --> <genericsetup:upgradeStep title="Set up intranet section" description="" source="001" destination="002" handler="pse12.initialcontent.upgrades.intranet_setup" sortkey="10" profile="pse12.initialcontent:default" />

<genericsetup:upgradeStep title="Set up the default pages" description="" source="001" destination="002" handler="pse12.initialcontent.upgrades.default_pages" sortkey="20" profile="pse12.initialcontent:default" />

PLONE SYMPOSIUM EAST 2012from zope.app.component.hooks import getSitefrom Products.CMFCore.utils import getToolByNamefrom sixfeetup.utils import helpers as sfutils

def intranet_setup(context): """Set up the placeful workflow for the intranet """ portal = getSite() # If the intranet doesn't exist, bail out if 'intranet' not in portal.objectIds(): return intranet = portal['intranet'] # If the placeful workflow is already in place, bail out if '.wf_policy_config' in intranet.objectIds(): return placeful_workflow = getToolByName(portal, 'portal_placeful_workflow') product = 'CMFPlacefulWorkflow' intranet.manage_addProduct[product].manage_addWorkflowPolicyConfig() config = placeful_workflow.getWorkflowPolicyConfig(intranet) policy = 'intranet' config.setPolicyBelow(policy=policy) config.setPolicyIn(policy=policy) # Make everything in the intranet `private` path = '/'.join(intranet.getPhysicalPath()) sfutils.publishEverything(context, path, 'hide')

PLONE SYMPOSIUM EAST 2012from zope.app.component.hooks import getSite

def default_pages(context): """There is a bug in quintagroup.transmogrifier that prevents the default page from being set. We will handle it here instead. """ portal = getSite() items = { 'about': dict( id='default_page', type='string', value='about-us'), 'blog': dict( id='layout', type='string', value='blog_view'), } for path, prop in items.items(): obj = portal.unrestrictedTraverse(path, None) # If the object doesn't exist, bail out if obj is None: continue target_obj = None if prop['id'] == 'default_page': target_obj = obj.unrestrictedTraverse(prop['value'], None) # Bail out if the default page target does not exist if target_obj is None: continue obj._setProperty(prop['id'], prop['value'], prop['value']) if target_obj is not None: # ensure that it doesn't show in the navigation target_obj.reindexObject()

Plone Site Recipe

PLONE SYMPOSIUM EAST 2012What is it?

• Create a Plone site

• Run profiles

• Re-create a site

PLONE SYMPOSIUM EAST 2012Add it to buildout

[buildout]parts = plonesite

[plonesite]recipe = collective.recipe.plonesiteinstance = instancezeoserver = zeoserversite-id = Ploneadmin-user = admin

PLONE SYMPOSIUM EAST 2012Add profiles

[plonesite]...profiles-initial = pse12.policy:initialprofiles = pse12.policy:default

PLONE SYMPOSIUM EAST 2012Buildout run

$ bin/buildout install plonesiteInstalling plonesite.Retrieved the admin userAdded Plone SiteQuick installing: []Running profiles: ['pse12.policy:initial']FinishedRunning profiles: ['pse12.policy:default', 'sixfeetup.customfolderalert:default', 'plone.app.debugtoolbar:default']

PLONE SYMPOSIUM EAST 2012Dynamic options

$ bin/buildout plonesite:enabled=false

$ bin/buildout plonesite:site-replace=true

Upgrade Demo

PLONE SYMPOSIUM EAST 2012Links

• sixieskel (http://github.com/sixfeetup/sixieskel)

• pse12-example-buildout (http://github.com/sixfeetup/pse12-example-buildout)

• pse12.policy (http://github.com/sixfeetup/pse12.policy)

• pse12.initialcontent (http://github.com/sixfeetup/pse12.initialcontent)

• quintagroup.transmogrifier (http://pypi.python.org/pypi/quintagroup.transmogrifier)

• collective.blog.star (http://pypi.python.org/pypi/collective.blog.star)

• plonetheme.transition (http://pypi.python.org/pypi/plonetheme.transition/)

PLONE SYMPOSIUM EAST 2012Photo Credits• http://www.flickr.com/photos/naturegeak/5642083189/ (who)

• https://secure.flickr.com/photos/campuspartymexico/5965708420/ (demo)

• https://secure.flickr.com/photos/aon/2171253511/ (realization)

• https://secure.flickr.com/photos/walkingsf/6930636483/ (policy)

• https://secure.flickr.com/photos/myklroventine/3261364899/ (GS)

• https://secure.flickr.com/photos/oskay/2157686638/ (add-ons)

• https://secure.flickr.com/photos/simonpholzman/5132795241/ (plone site)

• https://secure.flickr.com/photos/bitterjug/488731963// (upgrades)

• https://secure.flickr.com/photos/campuspartymexico/5965153009/ (upgrade)

• https://secure.flickr.com/photos/friarsbalsam/4609212148/ (content)

Thanks to

Check out

sixfeetup.com/demos

Questions?

Recommended