Running Zope/Plone scripts from the command line
Command line scripts get the global variable app from which you can get your portal which you use in place of self and context in scripts e.g.
app.mysite
But there is perhaps a more elegant way to do this, which we can combine with other bits we need to do, so keep reading.
Give your script a user
Command line scripts don't have a user, so if you need more than you can do as anonymous, then so this:
from AccessControl.SecurityManagement import newSecurityManager # Use Zope application server user database (not plone site) admin=app.acl_users.getUserById("admin") newSecurityManager(None, admin)
Replace "admin" with the user you need.
Create a fake HTTP request
Then you need to spoof the HTTP request object so you can traverse around and grab parts of the site. I had thought this would be irrelevant, but when I tried the script without it, I got skinnable errors, so it is probably required to do pretty much anything to your site. By all means try without it though.
import zope from AccessControl.SecurityManagement import newSecurityManager from AccessControl.SecurityManager import setSecurityPolicy from Testing.makerequest import makerequest from Products.CMFCore.tests.base.security import PermissiveSecurityPolicy, OmnipotentUser def spoofRequest(app): """ Make REQUEST variable to be available on the Zope application server. This allows acquisition to work properly """ _policy=PermissiveSecurityPolicy() _oldpolicy=setSecurityPolicy(_policy) newSecurityManager(None, OmnipotentUser().__of__(app.acl_users)) return makerequest(app) # Enable Faux HTTP request object app = spoofRequest(app) # Get Plone site object from Zope application server root site = app.unrestrictedTraverse("sitename") site.setupCurrentSkin(app.REQUEST) zope.app.component.hooks.setSite(site)
Add your code and commit any changes to the ZODB
Once you've done that, you're ready to put your code into the script. Create functions, like you would with an external method and pass site instead of self e.g.
def doMyThang(site):
If you make changes to the ZODB, you'll need to commit them manually as follows:
# Commit transaction import transaction transaction.commit() # Perform ZEO client synchronization (if runnning in clustered mode) app._p_jar.sync()
Put it all together
Putting all this together gives the following which can go at the top of your script, including the slightly more elegant way of getting the portal object
import zope import transaction from AccessControl.SecurityManagement import newSecurityManager from AccessControl.SecurityManager import setSecurityPolicy from Testing.makerequest import makerequest from Products.CMFCore.tests.base.security import PermissiveSecurityPolicy, OmnipotentUser # Use Zope application server user database (not plone site) admin=app.acl_users.getUserById("admin") newSecurityManager(None, admin) def spoofRequest(app): """ Make REQUEST variable to be available on the Zope application server. This allows acquisition to work properly """ _policy=PermissiveSecurityPolicy() _oldpolicy=setSecurityPolicy(_policy) newSecurityManager(None, OmnipotentUser().__of__(app.acl_users)) return makerequest(app) # Enable Faux HTTP request object app= spoofRequest(app) # Get Plone site object from Zope application server root site = app.unrestrictedTraverse("mysite") site.setupCurrentSkin(app.REQUEST) zope.app.component.hooks.setSite(site) # here's an example function def doSomeStuff(site): # go get something and change it, then... transaction.commit() app._p_jar.sync()
Run the script
Now you need the command to actually run the script. If the script is in Extensions, from inside the zeocluster that would be:
sudo ./bin/client2 run ./src/namespace.mypackage/namespace/mypackage/Extensions/script.py
And from cron this might be:
/usr/local/Plone-3.3.5/zeocluster/bin/client2 run /usr/local/Plone-3.3.5/zeocluster/src/namespace.mypackage/namespace/mypackage/Extensions/script.py 1>>/home/plone/sal_updates.log
This info came from the following page, where there also some extra stuff and also how to set up logging as well - http://collective-docs.plone.org/misc/commandline.html.
This no longer exists so try here http://readthedocs.org/docs/collective-docs/en/latest/misc/commandline.html
In the above the
import zope ... zope.app.component.hooks.setSite(site)
came from a similar error
zope.component.interfaces.ComponentLookupError: (<InterfaceClass
Products.CMFCore.interfaces._content.ISiteRoot>, '')
to the one encountered and fixed here
http://plone.293351.n2.nabble.com/plone-scripting-td6341705.html