From 9a5dc538c13c7f5d0db851b06028da6578c78ed0 Mon Sep 17 00:00:00 2001 From: "Tomasz J. Kotarba" Date: Thu, 19 Oct 2017 19:49:07 +0100 Subject: [PATCH] Initial commit --- Extensions/Install.py | 312 ++++++++++++++++++++++++++++++++ Extensions/__init__.py | 1 + ITickEvent.py | 55 ++++++ README.rst | 14 ++ TickEvent.py | 68 +++++++ TickingMachine.py | 212 ++++++++++++++++++++++ __init__.py | 111 ++++++++++++ config.py | 90 +++++++++ configure.zcml | 9 + doc/TickingMachine | 0 refresh.txt | 0 skins/TickingMachine/readme.txt | 8 + tool.gif | Bin 0 -> 339 bytes version.txt | 1 + 14 files changed, 881 insertions(+) create mode 100644 Extensions/Install.py create mode 100644 Extensions/__init__.py create mode 100644 ITickEvent.py create mode 100644 README.rst create mode 100644 TickEvent.py create mode 100644 TickingMachine.py create mode 100644 __init__.py create mode 100644 config.py create mode 100644 configure.zcml create mode 100644 doc/TickingMachine create mode 100644 refresh.txt create mode 100644 skins/TickingMachine/readme.txt create mode 100644 tool.gif create mode 100644 version.txt diff --git a/Extensions/Install.py b/Extensions/Install.py new file mode 100644 index 0000000..52a8768 --- /dev/null +++ b/Extensions/Install.py @@ -0,0 +1,312 @@ +# -*- coding: utf-8 -*- +# +# File: Install.py +# +# Copyright (c) 2007 by Tomasz J. Kotarba +# Generator: ArchGenXML Version 1.5.2 +# http://plone.org/products/archgenxml +# +# GNU General Public License (GPL) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. +# + +__author__ = """Tomasz J. Kotarba """ +__docformat__ = 'plaintext' + + +import os.path +import sys +from StringIO import StringIO +from sets import Set +from App.Common import package_home +from Products.CMFCore.utils import getToolByName +from Products.CMFCore.utils import manage_addTool +from Products.ExternalMethod.ExternalMethod import ExternalMethod +from zExceptions import NotFound, BadRequest + +from Products.Archetypes.Extensions.utils import installTypes +from Products.Archetypes.Extensions.utils import install_subskin +from Products.Archetypes.config import TOOL_NAME as ARCHETYPETOOLNAME +from Products.Archetypes.atapi import listTypes +from Products.TickingMachine.config import PROJECTNAME +from Products.TickingMachine.config import product_globals as GLOBALS + +def install(self, reinstall=False): + """ External Method to install TickingMachine """ + out = StringIO() + print >> out, "Installation log of %s:" % PROJECTNAME + + # If the config contains a list of dependencies, try to install + # them. Add a list called DEPENDENCIES to your custom + # AppConfig.py (imported by config.py) to use it. + try: + from Products.TickingMachine.config import DEPENDENCIES + except: + DEPENDENCIES = [] + portal = getToolByName(self,'portal_url').getPortalObject() + quickinstaller = portal.portal_quickinstaller + for dependency in DEPENDENCIES: + print >> out, "Installing dependency %s:" % dependency + quickinstaller.installProduct(dependency) + get_transaction().commit(1) + + classes = listTypes(PROJECTNAME) + installTypes(self, out, + classes, + PROJECTNAME) + install_subskin(self, out, GLOBALS) + + # autoinstall tools + portal = getToolByName(self,'portal_url').getPortalObject() + for t in ['TickingMachine']: + try: + portal.manage_addProduct[PROJECTNAME].manage_addTool(t) + except BadRequest: + # if an instance with the same name already exists this error will + # be swallowed. Zope raises in an unelegant manner a 'Bad Request' error + pass + except: + e = sys.exc_info() + if e[0] != 'Bad Request': + raise + + # hide tools in the search form + portalProperties = getToolByName(self, 'portal_properties', None) + if portalProperties is not None: + siteProperties = getattr(portalProperties, 'site_properties', None) + if siteProperties is not None and siteProperties.hasProperty('types_not_searched'): + for tool in ['TickingMachine']: + current = list(siteProperties.getProperty('types_not_searched')) + if tool not in current: + current.append(tool) + siteProperties.manage_changeProperties(**{'types_not_searched' : current}) + + # remove workflow for tools + portal_workflow = getToolByName(self, 'portal_workflow') + for tool in ['TickingMachine']: + portal_workflow.setChainForPortalTypes([tool], '') + + # uncatalog tools + for toolname in ['portal_tickingmachine']: + try: + portal[toolname].unindexObject() + except: + pass + + # hide tools in the navigation + portalProperties = getToolByName(self, 'portal_properties', None) + if portalProperties is not None: + navtreeProperties = getattr(portalProperties, 'navtree_properties', None) + if navtreeProperties is not None and navtreeProperties.hasProperty('idsNotToList'): + for toolname in ['portal_tickingmachine']: + current = list(navtreeProperties.getProperty('idsNotToList')) + if toolname not in current: + current.append(toolname) + navtreeProperties.manage_changeProperties(**{'idsNotToList' : current}) + + + # try to call a workflow install method + # in 'InstallWorkflows.py' method 'installWorkflows' + try: + installWorkflows = ExternalMethod('temp', 'temp', + PROJECTNAME+'.InstallWorkflows', + 'installWorkflows').__of__(self) + except NotFound: + installWorkflows = None + + if installWorkflows: + print >>out,'Workflow Install:' + res = installWorkflows(self,out) + print >>out,res or 'no output' + else: + print >>out,'no workflow install' + + + + # enable portal_factory for given types + factory_tool = getToolByName(self,'portal_factory') + factory_types=[ + "TickEvent", + "TickingMachine", + ] + factory_tool.getFactoryTypes().keys() + factory_tool.manage_setPortalFactoryTypes(listOfTypeIds=factory_types) + + from Products.TickingMachine.config import STYLESHEETS + try: + portal_css = getToolByName(portal, 'portal_css') + for stylesheet in STYLESHEETS: + try: + portal_css.unregisterResource(stylesheet['id']) + except: + pass + defaults = {'id': '', + 'media': 'all', + 'enabled': True} + defaults.update(stylesheet) + portal_css.registerStylesheet(**defaults) + except: + # No portal_css registry + pass + from Products.TickingMachine.config import JAVASCRIPTS + try: + portal_javascripts = getToolByName(portal, 'portal_javascripts') + for javascript in JAVASCRIPTS: + try: + portal_javascripts.unregisterResource(javascript['id']) + except: + pass + defaults = {'id': ''} + defaults.update(javascript) + portal_javascripts.registerScript(**defaults) + except: + # No portal_javascripts registry + pass + + # try to call a custom install method + # in 'AppInstall.py' method 'install' + try: + install = ExternalMethod('temp', 'temp', + PROJECTNAME+'.AppInstall', 'install') + except NotFound: + install = None + + if install: + print >>out,'Custom Install:' + try: + res = install(self, reinstall) + except TypeError: + res = install(self) + if res: + print >>out,res + else: + print >>out,'no output' + else: + print >>out,'no custom install' + return out.getvalue() + +def uninstall(self, reinstall=False): + out = StringIO() + + + # unhide tools in the search form + portalProperties = getToolByName(self, 'portal_properties', None) + if portalProperties is not None: + siteProperties = getattr(portalProperties, 'site_properties', None) + if siteProperties is not None and siteProperties.hasProperty('types_not_searched'): + for tool in ['TickingMachine']: + current = list(siteProperties.getProperty('types_not_searched')) + if tool in current: + current.remove(tool) + siteProperties.manage_changeProperties(**{'types_not_searched' : current}) + + + # unhide tools + portalProperties = getToolByName(self, 'portal_properties', None) + if portalProperties is not None: + navtreeProperties = getattr(portalProperties, 'navtree_properties', None) + if navtreeProperties is not None and navtreeProperties.hasProperty('idsNotToList'): + for toolname in ['portal_tickingmachine']: + current = list(navtreeProperties.getProperty('idsNotToList')) + if toolname in current: + current.remove(toolname) + navtreeProperties.manage_changeProperties(**{'idsNotToList' : current}) + + # try to call a workflow uninstall method + # in 'InstallWorkflows.py' method 'uninstallWorkflows' + try: + uninstallWorkflows = ExternalMethod('temp', 'temp', + PROJECTNAME+'.InstallWorkflows', + 'uninstallWorkflows').__of__(self) + except NotFound: + uninstallWorkflows = None + + if uninstallWorkflows: + print >>out, 'Workflow Uninstall:' + res = uninstallWorkflows(self, out) + print >>out, res or 'no output' + else: + print >>out,'no workflow uninstall' + + # try to call a custom uninstall method + # in 'AppInstall.py' method 'uninstall' + try: + uninstall = ExternalMethod('temp', 'temp', + PROJECTNAME+'.AppInstall', 'uninstall') + except: + uninstall = None + + if uninstall: + print >>out,'Custom Uninstall:' + try: + res = uninstall(self, reinstall) + except TypeError: + res = uninstall(self) + if res: + print >>out,res + else: + print >>out,'no output' + else: + print >>out,'no custom uninstall' + + return out.getvalue() + +def beforeUninstall(self, reinstall, product, cascade): + """ try to call a custom beforeUninstall method in 'AppInstall.py' + method 'beforeUninstall' + """ + out = StringIO() + try: + beforeuninstall = ExternalMethod('temp', 'temp', + PROJECTNAME+'.AppInstall', 'beforeUninstall') + except: + beforeuninstall = [] + + if beforeuninstall: + print >>out, 'Custom beforeUninstall:' + res = beforeuninstall(self, reinstall=reinstall + , product=product + , cascade=cascade) + if res: + print >>out, res + else: + print >>out, 'no output' + else: + print >>out, 'no custom beforeUninstall' + return (out,cascade) + +def afterInstall(self, reinstall, product): + """ try to call a custom afterInstall method in 'AppInstall.py' method + 'afterInstall' + """ + out = StringIO() + try: + afterinstall = ExternalMethod('temp', 'temp', + PROJECTNAME+'.AppInstall', 'afterInstall') + except: + afterinstall = None + + if afterinstall: + print >>out, 'Custom afterInstall:' + res = afterinstall(self, product=None + , reinstall=None) + if res: + print >>out, res + else: + print >>out, 'no output' + else: + print >>out, 'no custom afterInstall' + return out diff --git a/Extensions/__init__.py b/Extensions/__init__.py new file mode 100644 index 0000000..e784eb4 --- /dev/null +++ b/Extensions/__init__.py @@ -0,0 +1 @@ +# make me a python module diff --git a/ITickEvent.py b/ITickEvent.py new file mode 100644 index 0000000..f03cd82 --- /dev/null +++ b/ITickEvent.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- +# +# File: ITickEvent.py +# +# Copyright (c) 2007 by Tomasz J. Kotarba +# Generator: ArchGenXML Version 1.5.2 +# http://plone.org/products/archgenxml +# +# GNU General Public License (GPL) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. +# + +__author__ = """Tomasz J. Kotarba """ +__docformat__ = 'plaintext' + + +##code-section module-header #fill in your manual code here +##/code-section module-header + + + + +from zope.interface import Interface, Attribute + +class ITickEvent(Interface): + '''An event signaling a tick (vide the TickingMachine class). + ''' + + ##code-section class-header_ITickEvent #fill in your manual code here + date_time = Attribute("Time of the last tick") + next_tick = Attribute("Estimated time of the next tick") + ##/code-section class-header_ITickEvent + + + + +##code-section module-footer #fill in your manual code here +##/code-section module-footer + + + diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..f896e00 --- /dev/null +++ b/README.rst @@ -0,0 +1,14 @@ +=============== +Ticking Machine +=============== + +A Plone product used to create applications which can react to time events + +TickingMachine is a Plone product built using Archetypes and Zope3 framework. It adds the notion of a 'server tick' which is, basically, a simple time event triggered at specified intervals (set with a custom portal tool - portal_tickingmachine). This subsystem uses the observer pattern and zope3 events, which means that other subsystems can subscribe to it and get notified each time a tick takes place (thus being able to perceive the time flow and react to it by performing some actions). + +The TickingMachine needs an external clock source. You can use the cron daemon, Zope ClockServer or whatever you wish as long as it can invoke the tick() method of the TickingMachine portal tool (e.g. by accessing it via HTTP(s) with its URL). + +Tested with Plone 3.0, the product has proven to be production-ready while in use in one of the FTSE100 corporations. + + +Please note, I created this project for the now ancient version of Plone and stopped maintaining it in 2008 so it is probably of little use. I am putting it here for archiving purposes (too many of my projects were either lost or never open sourced). After I had stopped maintaining it, this product served as base for the then new collective.timedevents product (see: `here `_). diff --git a/TickEvent.py b/TickEvent.py new file mode 100644 index 0000000..391d517 --- /dev/null +++ b/TickEvent.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- +# +# File: TickEvent.py +# +# Copyright (c) 2007 by Tomasz J. Kotarba +# Generator: ArchGenXML Version 1.5.2 +# http://plone.org/products/archgenxml +# +# GNU General Public License (GPL) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. +# + +__author__ = """Tomasz J. Kotarba """ +__docformat__ = 'plaintext' + +##code-section module-header #fill in your manual code here +from DateTime import DateTime + +# tick_logger - begin +from zope.component import adapter +import logging +# tick_logger - end + +##/code-section module-header + +from Products.TickingMachine.ITickEvent import ITickEvent +import zope + +class TickEvent(object): + """This class implements the ITickEvent interface. + """ + # zope3 interfaces + zope.interface.implements(ITickEvent) + + ##code-section class-header_TickEvent #fill in your manual code here + def __init__(self, date_time, next_tick): + self.date_time = DateTime(date_time) + self.next_tick = DateTime(next_tick) + ##/code-section class-header_TickEvent + + +##code-section module-footer #fill in your manual code here + +@adapter(ITickEvent) +def tick_logger(tick_event): + """This function is a handler for the ITickEvent. Its purpose is to log all + ticks. + """ + l = logging.getLogger('TickingMachine') + l.info('(%s) TICK detected.' % tick_event.date_time.ISO()) + +##/code-section module-footer + + diff --git a/TickingMachine.py b/TickingMachine.py new file mode 100644 index 0000000..1d306a0 --- /dev/null +++ b/TickingMachine.py @@ -0,0 +1,212 @@ +# -*- coding: utf-8 -*- +# +# File: TickingMachine.py +# +# Copyright (c) 2007 by Tomasz J. Kotarba +# Generator: ArchGenXML Version 1.5.2 +# http://plone.org/products/archgenxml +# +# GNU General Public License (GPL) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. +# + +__author__ = """Tomasz J. Kotarba """ +__docformat__ = 'plaintext' + +from AccessControl import ClassSecurityInfo +from Products.Archetypes.atapi import * +from Products.TickingMachine.config import * + + +from Products.CMFCore.utils import UniqueObject + + +##code-section module-header #fill in your manual code here +from DateTime.DateTime import DateTime +from zope.event import notify +from TickEvent import TickEvent +##/code-section module-header + +schema = Schema(( + + #

Represents a time when the last tick took place.

+ DateTimeField( + name='lastTick', + widget=CalendarWidget( + label='Last tick', + label_msgid='TickingMachine_label_lastTick', + i18n_domain='TickingMachine', + ) + ), + + #

Represents expected interval between ticks (in seconds).

+ FloatField( + name='interval', + default=300.0, + required=1, + widget=FloatField._properties['widget']( + label='Time interval', + label_msgid='TickingMachine_label_interval', + i18n_domain='TickingMachine', + description='A real number representing expected interval ' \ + 'between ticks (in seconds).', + ) + ), + +), +) + +##code-section after-local-schema #fill in your manual code here +##/code-section after-local-schema + +TickingMachine_schema = BaseSchema.copy() + \ + schema.copy() + +##code-section after-schema #fill in your manual code here +##/code-section after-schema + +class TickingMachine(UniqueObject, BaseContent): + """This is a portal tool which tries to make a Plone site aware of the + passage of time, thus enabling it to react to time events. It does so by + dispatching so called 'tick events' (vide the TickEvent class) to all + its subscribers (via the zope3 event mechanism) each time its 'tick' + method executes. + The 'tick' method is a portal action and it should be triggered + periodically (e.g. by a simple wget script run by a cron daemon). + The tool stores a time of the last tick and also allows Plone site + administrators to set a time interval between 'ticks' which serves as a + safety mechanism to prevent the TickingMachine from unnecesserily + dispatching tick events to all its subscribers in case a frequency of + the tick action activation was too high. + """ + security = ClassSecurityInfo() + __implements__ = (getattr(UniqueObject,'__implements__',()),) + \ + (getattr(BaseContent,'__implements__',()),) + + # This name appears in the 'add' box + archetype_name = 'TickingMachine' + + meta_type = 'TickingMachine' + portal_type = 'TickingMachine' + allowed_content_types = [] + filter_content_types = 0 + global_allow = 0 + #content_icon = 'TickingMachine.gif' + immediate_view = 'base_view' + default_view = 'base_view' + suppl_views = () + typeDescription = "TickingMachine" + typeDescMsgId = 'description_edit_tickingmachine' + #toolicon = 'TickingMachine.gif' + + + actions = ( + + + {'action': "string:${object_url}/tick", + 'category': "object", + 'id': 'tick', + 'name': 'tick', + 'permissions': ("View",), + 'condition': 'python:1' + }, + + + ) + + _at_rename_after_creation = True + + schema = TickingMachine_schema + + ##code-section class-header #fill in your manual code here + ##/code-section class-header + + + # tool-constructors have no id argument, the id is fixed + def __init__(self, id=None): + BaseContent.__init__(self,'portal_tickingmachine') + self.setTitle('TickingMachine') + + ##code-section constructor-footer #fill in your manual code here + ##/code-section constructor-footer + + + # tool should not appear in portal_catalog + def at_post_edit_script(self): + self.unindexObject() + + ##code-section post-edit-method-footer #fill in your manual code here + ##/code-section post-edit-method-footer + + + # Methods + #security.declarePublic('tick') + def tick(self): + """This method is a portal action responsible for deciding whether to + dispatch tick events to the TickEvent subscribers. It should be + triggered periodically using its URL (e.g. by a simple wget script + run by a cron daemon). + """ + + # Check current time. + current = DateTime() + + # Get lastTick. If it is invalid, set it to the minimum possible value. + last = self.getLastTick() + if not isinstance(last, DateTime): + last = DateTime(0) + + # Get interval. Make sure the value used here is no lesser than 0. + interval = self.getInterval() + if interval < 0: + interval = 0 + + # If current time less lastTick is equal to or greater than + # (0.9 * interval) then set lastTick to the current time and + # execute _notify(). Otherwise do nothing. + if current.timeTime() - last.timeTime() >= 0.9 * interval: + self.setLastTick(current) + notify(TickEvent(current,self.getNextTickEstimation( + last_tick=current,interval=interval))) + + + def getNextTickEstimation(self, last_tick=None, interval=None): + """This method tries to estimate a time when a next tick will occur. + Then it returns a DateTime object representing that time. + """ + if last_tick is None: + last_tick = DateTime(self.getLastTick()) + + if interval is None: + interval = float(self.getInterval()) + + # unit conversion - seconds to days (needed by DateTime) + interval = interval / 86400.0 + + # compute a DateTime object for the estimated time + next_tick_estimation = last_tick + interval + + return next_tick_estimation + +registerType(TickingMachine, PROJECTNAME) +# end of class TickingMachine + +##code-section module-footer #fill in your manual code here +##/code-section module-footer + + + diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..9d5de67 --- /dev/null +++ b/__init__.py @@ -0,0 +1,111 @@ +# -*- coding: utf-8 -*- +# +# File: TickingMachine.py +# +# Copyright (c) 2007 by Tomasz J. Kotarba +# Generator: ArchGenXML Version 1.5.2 +# http://plone.org/products/archgenxml +# +# GNU General Public License (GPL) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. +# + +__author__ = """Tomasz J. Kotarba """ +__docformat__ = 'plaintext' + + +# There are three ways to inject custom code here: +# +# - To set global configuration variables, create a file AppConfig.py. +# This will be imported in config.py, which in turn is imported in +# each generated class and in this file. +# - To perform custom initialisation after types have been registered, +# use the protected code section at the bottom of initialize(). +# - To register a customisation policy, create a file CustomizationPolicy.py +# with a method register(context) to register the policy. + +import logging +logger = logging.getLogger('TickingMachine') +logger.info('Installing Product') + +try: + import CustomizationPolicy +except ImportError: + CustomizationPolicy = None + +import os, os.path +from Globals import package_home +from Products.CMFCore import utils as cmfutils + +try: # New CMF + from Products.CMFCore import permissions as CMFCorePermissions +except: # Old CMF + from Products.CMFCore import CMFCorePermissions + +from Products.CMFCore import DirectoryView +from Products.CMFPlone.utils import ToolInit +from Products.Archetypes.atapi import * +from Products.Archetypes import listTypes +from Products.Archetypes.utils import capitalize +from config import * + +DirectoryView.registerDirectory('skins', product_globals) +DirectoryView.registerDirectory('skins/TickingMachine', + product_globals) + +##code-section custom-init-head #fill in your manual code here +##/code-section custom-init-head + + +def initialize(context): + ##code-section custom-init-top #fill in your manual code here + ##/code-section custom-init-top + + # imports packages and types for registration + + import TickEvent + import TickingMachine + import ITickEvent + + # Initialize portal tools + tools = [TickingMachine.TickingMachine] + ToolInit( PROJECTNAME +' Tools', + tools = tools, + icon='tool.gif' + ).initialize( context ) + + # Initialize portal content + content_types, constructors, ftis = process_types( + listTypes(PROJECTNAME), + PROJECTNAME) + + cmfutils.ContentInit( + PROJECTNAME + ' Content', + content_types = content_types, + permission = DEFAULT_ADD_CONTENT_PERMISSION, + extra_constructors = constructors, + fti = ftis, + ).initialize(context) + + # Apply customization-policy, if theres any + if CustomizationPolicy and hasattr(CustomizationPolicy, 'register'): + CustomizationPolicy.register(context) + print 'Customization policy for TickingMachine installed' + + ##code-section custom-init-bottom #fill in your manual code here + ##/code-section custom-init-bottom + diff --git a/config.py b/config.py new file mode 100644 index 0000000..08ac2f9 --- /dev/null +++ b/config.py @@ -0,0 +1,90 @@ +# -*- coding: utf-8 -*- +# +# File: TickingMachine.py +# +# Copyright (c) 2007 by Tomasz J. Kotarba +# Generator: ArchGenXML Version 1.5.2 +# http://plone.org/products/archgenxml +# +# GNU General Public License (GPL) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. +# + +__author__ = """Tomasz J. Kotarba """ +__docformat__ = 'plaintext' + + +# Product configuration. +# +# The contents of this module will be imported into __init__.py, the +# workflow configuration and every content type module. +# +# If you wish to perform custom configuration, you may put a file +# AppConfig.py in your product's root directory. This will be included +# in this file if found. + +try: # New CMF + from Products.CMFCore.permissions import setDefaultRoles +except ImportError: # Old CMF + from Products.CMFCore.CMFCorePermissions import setDefaultRoles + + +##code-section config-head #fill in your manual code here +##/code-section config-head + + +PROJECTNAME = "TickingMachine" + +# Check for Plone 2.1 +try: + from Products.CMFPlone.migrations import v2_1 +except ImportError: + HAS_PLONE21 = False +else: + HAS_PLONE21 = True + +# Permissions +DEFAULT_ADD_CONTENT_PERMISSION = "Add portal content" +setDefaultRoles(DEFAULT_ADD_CONTENT_PERMISSION, ('Manager', 'Owner')) + +product_globals = globals() + +# Dependencies of Products to be installed by quick-installer +# override in custom configuration +DEPENDENCIES = [] + +# Dependend products - not quick-installed - used in testcase +# override in custom configuration +PRODUCT_DEPENDENCIES = [] + +# You can overwrite these two in an AppConfig.py: +# STYLESHEETS = [{'id': 'my_global_stylesheet.css'}, +# {'id': 'my_contenttype.css', +# 'expression': 'python:object.getTypeInfo().getId() == "MyType"'}] +# You can do the same with JAVASCRIPTS. +STYLESHEETS = [] +JAVASCRIPTS = [] + +##code-section config-bottom #fill in your manual code here +##/code-section config-bottom + + +# Load custom configuration not managed by ArchGenXML +try: + from Products.TickingMachine.AppConfig import * +except ImportError: + pass diff --git a/configure.zcml b/configure.zcml new file mode 100644 index 0000000..fc5387a --- /dev/null +++ b/configure.zcml @@ -0,0 +1,9 @@ + + + + + diff --git a/doc/TickingMachine b/doc/TickingMachine new file mode 100644 index 0000000..e69de29 diff --git a/refresh.txt b/refresh.txt new file mode 100644 index 0000000..e69de29 diff --git a/skins/TickingMachine/readme.txt b/skins/TickingMachine/readme.txt new file mode 100644 index 0000000..1e15168 --- /dev/null +++ b/skins/TickingMachine/readme.txt @@ -0,0 +1,8 @@ +Directory 'skins/TickingMachine': + +Put your templates, css and javascript files in here. When first +installed, this skin layer is added to the plone skin. It is added +right below the 'custom' layer. Later, other products can move it a +little bit down, but it'll always be above the plone skin layers. So: +you can use it to overwrite plone stuff. + diff --git a/tool.gif b/tool.gif new file mode 100644 index 0000000000000000000000000000000000000000..a290b3f98fe8fe0e9e35deefd6917d4e848c97fb GIT binary patch literal 339 zcmZ?wbhEHb6krfwSjxcQmR?*ueQD{8WfgN)R?k~~m+@HRl8wzvH?^(Y+PQjL@48(Z zj-TIp^7778mv)`LwByX>9cQlYId^sM`D=U6-`Icg=FyAS4_vx=^wN!^S8pG`e(%Kf zyC<&SJ9*>og}aX~-FdM#v literal 0 HcmV?d00001 diff --git a/version.txt b/version.txt new file mode 100644 index 0000000..d3827e7 --- /dev/null +++ b/version.txt @@ -0,0 +1 @@ +1.0