cyberapps.commerce: Python3 fixes

This commit is contained in:
Helmut Merz 2024-10-01 15:44:21 +02:00
parent 3524df21a1
commit c6c4fd56f2
27 changed files with 1554 additions and 0 deletions

7
cyberapps/bsm/README.txt Normal file
View file

@ -0,0 +1,7 @@
========================
Berlin School Management
========================
School Information
==================

View file

@ -0,0 +1,3 @@
"""
$Id$
"""

138
cyberapps/bsm/browser.py Normal file
View file

@ -0,0 +1,138 @@
#
# Copyright (c) 2007 Helmut Merz helmutm@cy55.de
#
# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""
View classes for the BSM (Berlin School Management) project.
$Id$
"""
from zope import interface, component
from zope.app.pagetemplate import ViewPageTemplateFile
from zope.cachedescriptors.property import Lazy
from cybertools.composer.schema import Schema
from cybertools.composer.schema import Field
from cybertools.reporter.browser.report import DetailView, ListingView
from cybertools.reporter.resultset import ResultSet, Cell
from loops.browser.concept import ConceptView
from loops.browser.node import NodeView
from loops.common import adapted
from loops.browser.common import conceptMacrosTemplate
from loops import util
bsmTemplate = ViewPageTemplateFile('macros.pt')
class TitleCell(Cell):
@property
def url(self):
view = self.row.resultSet.view
if view is None:
return u''
return ('%s/.target%s' %
(view.url, util.getUidForObject(self.row.context.context)))
class UrlCell(Cell):
limit = None
@property
def url(self):
value = self.value
if not value:
return ''
if self.field.name == 'email':
return 'mailto:' + value
if value.startswith('http'):
return value
return 'http://' + value
@property
def text(self):
text = super(UrlCell, self).text
if self.limit and len(text) > self.limit:
text = text[:self.limit-1] + '...'
return text
class UrlCellWithLimit(UrlCell):
limit = 20
class SchoolDetails(DetailView):
conceptMacros = conceptMacrosTemplate
@property
def macro(self):
return bsmTemplate.macros['detail']
@Lazy
def nodeView(self):
return NodeView(self.context, self.request)
def resources(self):
for obj in self.context.getResources():
yield ConceptView(obj, self.request)
@Lazy
def resultSet(self):
result = ResultSet([adapted(self.context)])
result.schema = Schema(
Field(u'title', u'Name'),
Field(u'address', u'Anschrift'),
Field(u'headMaster', u'Rektor'),
Field(u'telephone', u'Telefon'),
Field(u'telefax', u'Telefax'),
Field(u'email', u'E-Mail', renderFactory=UrlCell),
Field(u'website', u'Web', renderFactory=UrlCell),
)
result.view = self.nodeView
return result
class SchoolListing(ListingView):
@property
def children(self):
for obj in self.nodeView.virtualTargetObject.getChildren():
yield adapted(obj)
@Lazy
def nodeView(self):
return NodeView(self.context, self.request)
@Lazy
def resultSet(self):
result = ResultSet(self.children)
result.schema = Schema(
Field(u'title', u'Name', renderFactory=TitleCell),
Field(u'address', u'Anschrift'),
Field(u'headMaster', u'Rektor'),
Field(u'telephone', u'Telefon'),
Field(u'telefax', u'Telefax'),
Field(u'email', u'E-Mail', renderFactory=UrlCellWithLimit),
Field(u'website', u'Web', renderFactory=UrlCellWithLimit),
)
result.view = self.nodeView
return result

View file

@ -0,0 +1,32 @@
<!-- $Id$ -->
<configure
xmlns:zope="http://namespaces.zope.org/zope"
xmlns:browser="http://namespaces.zope.org/browser"
i18n_domain="zope">
<zope:adapter factory="cyberapps.bsm.data.SchoolInfoAdapter"
trusted="True" />
<zope:class class="cyberapps.bsm.data.SchoolInfoAdapter">
<require permission="zope.View"
interface="cyberapps.bsm.interfaces.ISchoolInfo" />
<require permission="zope.ManageContent"
set_schema="cyberapps.bsm.interfaces.ISchoolInfo" />
</zope:class>
<browser:page
for="loops.interfaces.IConcept"
name="bsm_school_detail.html"
class="cyberapps.bsm.browser.SchoolDetails"
permission="zope.View"
/>
<browser:page
for="loops.interfaces.INode"
name="bsm_schools.html"
class="cyberapps.bsm.browser.SchoolListing"
permission="zope.View"
/>
</configure>

42
cyberapps/bsm/data.py Normal file
View file

@ -0,0 +1,42 @@
#
# Copyright (c) 2007 Helmut Merz helmutm@cy55.de
#
# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""
Classes for BSM School Informations.
$Id$
"""
from zope.component import adapts
from zope.interface import implements
from cyberapps.bsm.interfaces import ISchoolInfo
from loops.common import AdapterBase
from loops.interfaces import IConcept
from loops.type import TypeInterfaceSourceList
TypeInterfaceSourceList.typeInterfaces += (ISchoolInfo,)
class SchoolInfoAdapter(AdapterBase):
implements(ISchoolInfo)
_contextAttributes = list(ISchoolInfo) + list(IConcept)

View file

@ -0,0 +1,62 @@
#
# Copyright (c) 2007 Helmut Merz helmutm@cy55.de
#
# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""
Interfaces for BSM (Berlin School Managment).
$Id$
"""
from zope.interface import Interface, Attribute
from zope import schema
from loops.util import _
class ISchoolInfo(Interface):
""" Information about schools taking part in the Berlin School Management
(BSM) project.
The name of the school is in the ``title`` attribute.
"""
address = schema.TextLine(
title=_(u'Address'),
description=_(u'Postal address of the school'),
required=False,)
headMaster = schema.TextLine(
title=_(u'Headmaster'),
description=_(u'Name of the head master of the school'),
required=False,)
telephone = schema.TextLine(
title=_(u'Telephone'),
description=_(u'Telephone number of the headmaster'),
required=False,)
telefax = schema.TextLine(
title=_(u'Telefax'),
description=_(u'Telefax number of the headmaster'),
required=False,)
email = schema.TextLine(
title=_(u'Email'),
description=_(u'Email address of the headmaster'),
required=False,)
website = schema.TextLine(
title=_(u'Website'),
description=_(u'URL of the website of the school'),
required=False,)

30
cyberapps/bsm/macros.pt Normal file
View file

@ -0,0 +1,30 @@
<!-- $Id$ -->
<metal:detail define-macro="detail"
tal:define="result item/resultSet">
<h2 tal:content="item/title"
tal:attributes="ondblclick item/openEditWindow">
Something</h2><br />
<table>
<tr tal:repeat="cell item/cells">
<td width="20%">
<span tal:content="cell/field/title"
i18n:translate="">Fieldname</span>:
</td>
<td>
<a href="#"
tal:omit-tag="not:cell/url"
tal:attributes="href cell/url;
title cell/urlTitle">
<span tal:content="cell/text">Value</span>
</a>
</td>
</tr>
</table><br />
<div tal:attributes="class string:content-$level;">
<metal:fields use-macro="item/conceptMacros/macros/conceptresources" />
</div>
</metal:detail>

View file

@ -0,0 +1,187 @@
====================
eCommerce with loops
====================
Note: This package depends on loops.
Let's do some basic set up
>>> from zope.app.testing.setup import placefulSetUp, placefulTearDown
>>> site = placefulSetUp(True)
>>> from zope import component, interface
and setup a simple loops site with a concept manager and some concepts
(with all the type machinery, what in real life is done via standard
ZCML setup):
>>> from cyberapps.commerce.tests import TestSite
>>> t = TestSite(site)
>>> concepts, resources, views = t.setup()
We also collect here some basic imports we'll need later.
>>> from loops.concept import Concept
>>> from loops.common import adapted
>>> from loops.setup import addAndConfigureObject
We also use an adapter to the concept manager for accessing the commerce
objects.
>>> from cyberapps.commerce.manager import Manager
>>> manager = Manager(concepts)
Shops and Products
==================
Let's start with two shops:
>>> shop1 = manager.shops.create('shop1', title='PC up Ltd')
>>> shop2 = manager.shops.create('shop2', title='Video up Ltd')
>>> len(list(manager.shops))
2
>>> shop1.title
'PC up Ltd'
Now we create a few products ...
>>> p001 = manager.products.create('001', title='Silent Case')
>>> p002 = manager.products.create('002', title='Portable Projector')
>>> p003 = manager.products.create('003', title='HD Flatscreen Monitor')
>>> p004 = manager.products.create('004', title='Giga Mainboard')
>>> p001.title
'Silent Case'
>>> p001.fullDescription
... and add them to the shops.
>>> shop1.products.add(p001)
>>> shop1.products.add(p003)
>>> shop1.products.add(p004)
>>> shop2.products.add(p002)
>>> shop2.products.add(p003)
We can now list the products in a shop.
>>> sorted((p.productId, p.title) for p in shop1.products)
[('001', 'Silent Case'), ('003', 'HD Flatscreen Monitor'),
('004', 'Giga Mainboard')]
>>> sorted((s.name, s.title) for s in p003.shops)
[('shop1', 'PC up Ltd'), ('shop2', 'Video up Ltd')]
Categories
----------
>>> cat001 = manager.categories.create('001', title='Cases')
>>> p001.categories.add(cat001)
Customers
=========
Now let's add a few customers.
>>> c001 = manager.customers.create('001', title='Your Local Computer Store')
>>> c002 = manager.customers.create('002', title='Speedy Gonzales')
>>> c003 = manager.customers.create('003', title='TeeVee')
>>> c004 = manager.customers.create('004', title='MacVideo')
These are stored in a separate ConceptManager object.
>>> customers = concepts.getLoopsRoot()['customers']
>>> len(customers)
4
In the testing scenario we have to index all the customer objects.
>>> from zope.app.catalog.interfaces import ICatalog
>>> catalog = component.getUtility(ICatalog)
>>> from loops import util
>>> for r in customers.values():
... catalog.index_doc(int(util.getUidForObject(r)), r)
Now the customers can be accessed via the standard concept manager adapter.
>>> manager.customers.get('004')
<cyberapps.commerce.customer.Customer object ...>
>>> shop1.customers.add(c001)
>>> shop1.customers.add(c002)
>>> shop1.customers.add(c004)
>>> shop2.customers.add(c002)
>>> shop2.customers.add(c003)
>>> shop2.customers.add(c004)
>>> sorted((c.customerId, c.title) for c in shop1.customers)
[('001', 'Your Local Computer Store'), ('002', 'Speedy Gonzales'),
('004', 'MacVideo')]
>>> sorted((s.name, s.title) for s in c002.shops)
[('shop1', 'PC up Ltd'), ('shop2', 'Video up Ltd')]
Carts and Orders
================
A cart is just a collection of order items belonging to a certain customer
(or some other kind of party).
>>> orderItems = manager.orderItems
>>> orderItems.add(p001, c001, shop=shop1, quantity=3)
<OrderItem ['44', 1, '60', '... ...', '???']: {'quantity': 3, 'shop': '40'}>
>>> orderItems.getCart(c001)
[<OrderItem ['44', 1, '60', '... ...', '???']: {'quantity': 3, 'shop': '40'}>]
Orders
------
The items in a shopping cart may be included in an order.
>>> ord001 = manager.orders.create('001', shop=shop1, customer=c001)
>>> for item in orderItems.getCart(c001):
... item.setOrder(ord001)
Now the default cart is empty; we have to supply the order for
retrieving the order items. But now we can omit the customer from the query.
>>> orderItems.getCart(c001)
[]
>>> orderItems.getCart(c001, ord001)
[<OrderItem ['44', 1, '60', '... ...', '74']: {'quantity': 3, 'shop': '40'}>]
>>> orderItems.getCart(order=ord001)
[<OrderItem ['44', 1, '60', '... ...', '74']: {'quantity': 3, 'shop': '40'}>]
Administrative Views and Forms
==============================
>>> from zope.publisher.browser import TestRequest
Listings
--------
>>> from cyberapps.commerce.browser.base import SimpleListing
Forms
-----
>>> from loops.view import Node
>>> from cyberapps.commerce.browser.product import CreateProductPage
>>> home = addAndConfigureObject(views, Node, 'home', target=cat001.context)
>>> form = CreateProductPage(home, TestRequest())
Fin de partie
=============
>>> placefulTearDown()

View file

@ -0,0 +1,3 @@
"""
$Id$
"""

View file

@ -0,0 +1,3 @@
"""
$Id$
"""

View file

@ -0,0 +1,58 @@
#
# Copyright (c) 2009 Helmut Merz helmutm@cy55.de
#
# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""
Action definitions for loops-based eCommerce applications.
$Id$
"""
from cyberapps.commerce.util import _
from cybertools.browser.action import actions
from loops.browser.action import DialogAction, TargetAction
actions.register('create_product', 'portlet', TargetAction,
title=_(u'Create Product...'),
description=_(u'Create a new product.'),
viewName='create_product_page.html',
)
actions.register('edit_product', 'portlet', TargetAction,
title=_(u'Edit Product...'),
description=_(u'Modify product data.'),
viewName='edit_product_page.html',
)
actions.register('create_category', 'portlet', TargetAction,
title=_(u'Create Catgory...'),
description=_(u'Create a new category.'),
viewName='create_category_page.html',
)
actions.register('edit_category', 'portlet', TargetAction,
title=_(u'Edit Category...'),
description=_(u'Modify category data.'),
viewName='edit_category_page.html',
)
actions.register('edit_customer', 'portlet', TargetAction,
title=_(u'Edit Customer...'),
description=_(u'Modify customer data.'),
viewName='edit_concept_page.html',
)

View file

@ -0,0 +1,22 @@
<!-- $Id$ -->
<metal:block define-macro="simple_listing">
<div>
<metal:title use-macro="item/conceptMacros/concepttitle" />
</div>
<metal:listing define-macro="base_listing">
<dl tal:repeat="related item/children">
<dt>
<a tal:content="related/title"
tal:attributes="href python: view.getUrlForTarget(related);">Something</a>
</dt>
<dd class="description"
tal:define="description related/description"
tal:condition="description">
<span tal:content="structure related/renderedDescription">Description</span>
</dd>
</dl>
</metal:listing>
</metal:block>

View file

@ -0,0 +1,41 @@
#
# Copyright (c) 2009 Helmut Merz helmutm@cy55.de
#
# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""
Common base/common view classes for loops-based eCommerce applications.
$Id$
"""
from zope.app.pagetemplate import ViewPageTemplateFile
from zope.cachedescriptors.property import Lazy
from zope import component
from loops.browser.concept import ConceptView
from loops.common import adapted
from loops import util
base_macros = ViewPageTemplateFile('base.pt')
class SimpleListing(ConceptView):
@Lazy
def macro(self):
return base_macros.macros['simple_listing']

View file

@ -0,0 +1,84 @@
<!-- $Id$ -->
<configure
xmlns:zope="http://namespaces.zope.org/zope"
xmlns:browser="http://namespaces.zope.org/browser"
i18n_domain="cyberapps.commerce">
<zope:module module="cyberapps.commerce.browser.action" />
<!-- products, categories, ... -->
<zope:adapter
name="commerce_simple_listing.html"
for="loops.interfaces.IConcept
zope.publisher.interfaces.browser.IBrowserRequest"
provides="zope.interface.Interface"
factory="cyberapps.commerce.browser.base.SimpleListing"
permission="zope.View" />
<zope:adapter
name="commerce_product.html"
for="loops.interfaces.IConcept
zope.publisher.interfaces.browser.IBrowserRequest"
provides="zope.interface.Interface"
factory="cyberapps.commerce.browser.product.ProductView"
permission="zope.View" />
<zope:adapter
name="commerce_category.html"
for="loops.interfaces.IConcept
zope.publisher.interfaces.browser.IBrowserRequest"
provides="zope.interface.Interface"
factory="cyberapps.commerce.browser.product.CategoryView"
permission="zope.View" />
<browser:page
name="create_product_page.html"
for="loops.interfaces.INode"
class="cyberapps.commerce.browser.product.CreateProductPage"
permission="zope.ManageContent" />
<browser:page
name="edit_product_page.html"
for="loops.interfaces.INode"
class="cyberapps.commerce.browser.product.EditProductPage"
permission="zope.ManageContent" />
<zope:adapter
name="create_product"
for="loops.browser.node.NodeView
zope.publisher.interfaces.browser.IBrowserRequest"
factory="cyberapps.commerce.browser.product.CreateProduct"
permission="zope.ManageContent" />
<browser:page
name="create_category_page.html"
for="loops.interfaces.INode"
class="cyberapps.commerce.browser.product.CreateCategoryPage"
permission="zope.ManageContent" />
<browser:page
name="edit_category_page.html"
for="loops.interfaces.INode"
class="cyberapps.commerce.browser.product.EditCategoryPage"
permission="zope.ManageContent" />
<zope:adapter
name="create_category"
for="loops.browser.node.NodeView
zope.publisher.interfaces.browser.IBrowserRequest"
factory="cyberapps.commerce.browser.product.CreateCategory"
permission="zope.ManageContent" />
<!-- customers, ... -->
<zope:adapter
name="commerce_customer.html"
for="loops.interfaces.IConcept
zope.publisher.interfaces.browser.IBrowserRequest"
provides="zope.interface.Interface"
factory="cyberapps.commerce.browser.customer.CustomerView"
permission="zope.View" />
</configure>

View file

@ -0,0 +1,37 @@
#
# Copyright (c) 2009 Helmut Merz helmutm@cy55.de
#
# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""
View classes for customers and related objects.
$Id$
"""
from zope.app.pagetemplate import ViewPageTemplateFile
from zope.cachedescriptors.property import Lazy
from loops.browser.concept import ConceptView
from loops.common import adapted
from loops import util
# customers
class CustomerView(ConceptView):
pass

View file

@ -0,0 +1,43 @@
<!-- $Id$ -->
<metal:block define-macro="category">
<div>
<metal:title use-macro="item/conceptMacros/concepttitle" />
</div>
<h2 i18n:translate="">Subcategories</h2>
<dl tal:repeat="related item/subcategories">
<tal:item define="data related/data">
<dt>
<a tal:content="related/title"
tal:attributes="href python: view.getUrlForTarget(related);">Category</a>
</dt>
<dd class="description"
tal:define="description related/description"
tal:condition="description">
<span tal:content="structure related/renderedDescription">Description</span>
</dd>
</tal:item>
</dl>
<h2 i18n:translate="">Products</h2>
<dl tal:repeat="related item/products">
<tal:item define="data related/data">
<dt>
<a tal:content="related/title"
tal:attributes="href python: view.getUrlForTarget(related);">Product</a>
</dt>
<dd class="description"
tal:define="description related/description"
tal:condition="description">
<span tal:content="structure related/renderedDescription">Description</span>
</dd>
</tal:item>
</dl>
<metal:resources use-macro="view/concept_macros/conceptresources" />
</metal:block>

View file

@ -0,0 +1,130 @@
#
# Copyright (c) 2009 Helmut Merz helmutm@cy55.de
#
# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""
View classes for loops-based eCommerce applications.
$Id$
"""
from zope.app.pagetemplate import ViewPageTemplateFile
from zope.cachedescriptors.property import Lazy
from zope import component
from cybertools.browser.action import actions
from cybertools.composer.schema import Schema
from cybertools.composer.schema import Field
from loops.browser.action import DialogAction, TargetAction
from loops.browser.concept import ConceptView
from loops.browser.form import EditConceptPage, CreateConceptPage
from loops.browser.form import EditConcept, CreateConcept
from loops.browser.node import NodeView
from loops.common import adapted
from loops import util
product_macros = ViewPageTemplateFile('product.pt')
# products
class ProductView(ConceptView):
pass
class EditProductPage(EditConceptPage):
showAssignments = False
def setupController(self):
super(EditProductPage, self).setupController()
self.registerDojoFormAllGrid()
class CreateProductPage(CreateConceptPage):
showAssignments = False
typeToken = '.loops/concepts/product'
fixedType = True
form_action = 'create_product'
def setupController(self):
super(CreateProductPage, self).setupController()
self.registerDojoFormAllGrid()
class CreateProduct(CreateConcept):
def getNameFromData(self):
id = self.request.form.get('productId')
if id:
return 'p' + id
# categories
class CategoryView(ConceptView):
@Lazy
def macro(self):
return product_macros.macros['category']
@Lazy
def subcategories(self):
for c in self.adapted.subcategories:
view = ConceptView(c.context, self.request)
yield view
@Lazy
def products(self):
for c in self.adapted.products:
view = ProductView(c.context, self.request)
yield view
class EditCategoryPage(EditConceptPage):
#showAssignments = False
def setupController(self):
super(EditCategoryPage, self).setupController()
self.registerDojoFormAllGrid()
class CreateCategoryPage(CreateConceptPage):
#showAssignments = False
typeToken = '.loops/concepts/category'
fixedType = True
form_action = 'create_category'
def setupController(self):
super(CreateCategoryPage, self).setupController()
self.registerDojoFormAllGrid()
class CreateCategory(CreateConcept):
def getNameFromData(self):
return super(CreateCategory, self).getNameFromData()
id = self.request.form.get('productId')
if id:
return 'p' + id

View file

@ -0,0 +1,95 @@
<!-- $Id$ -->
<configure
xmlns:zope="http://namespaces.zope.org/zope"
xmlns:browser="http://namespaces.zope.org/browser"
i18n_domain="cyberapps.commerce">
<include package="cybertools.commerce" />
<zope:adapter factory="cyberapps.commerce.shop.Shop"
provides="cyberapps.commerce.interfaces.IShop"
trusted="True" />
<zope:class class="cyberapps.commerce.shop.Shop">
<require permission="zope.View"
interface="cyberapps.commerce.interfaces.IShop" />
<require permission="zope.ManageContent"
set_schema="cyberapps.commerce.interfaces.IShop" />
</zope:class>
<zope:adapter factory="cyberapps.commerce.product.Product"
provides="cyberapps.commerce.interfaces.IProduct"
trusted="True" />
<zope:class class="cyberapps.commerce.product.Product">
<require permission="zope.View"
interface="cyberapps.commerce.interfaces.IProduct" />
<require permission="zope.ManageContent"
set_schema="cyberapps.commerce.interfaces.IProduct" />
</zope:class>
<zope:adapter factory="cyberapps.commerce.product.Category"
provides="cyberapps.commerce.interfaces.ICategory"
trusted="True" />
<zope:class class="cyberapps.commerce.product.Category">
<require permission="zope.View"
interface="cyberapps.commerce.interfaces.ICategory" />
<require permission="zope.ManageContent"
set_schema="cyberapps.commerce.interfaces.ICategory" />
</zope:class>
<zope:adapter factory="cyberapps.commerce.product.Manufacturer"
trusted="True" />
<zope:class class="cyberapps.commerce.product.Manufacturer">
<require permission="zope.View"
interface="cybertools.commerce.interfaces.IManufacturer" />
<require permission="zope.ManageContent"
set_schema="cybertools.commerce.interfaces.IManufacturer" />
</zope:class>
<zope:adapter factory="cyberapps.commerce.customer.Customer"
provides="cyberapps.commerce.interfaces.ICustomer"
trusted="True" />
<zope:class class="cyberapps.commerce.customer.Customer">
<require permission="zope.View"
interface="cyberapps.commerce.interfaces.ICustomer" />
<require permission="zope.ManageContent"
set_schema="cyberapps.commerce.interfaces.ICustomer" />
</zope:class>
<zope:adapter factory="cyberapps.commerce.customer.Address"
provides="cyberapps.commerce.interfaces.IAddress"
trusted="True" />
<zope:class class="cyberapps.commerce.customer.Address">
<require permission="zope.View"
interface="cyberapps.commerce.interfaces.IAddress" />
<require permission="zope.ManageContent"
set_schema="cyberapps.commerce.interfaces.IAddress" />
</zope:class>
<zope:adapter factory="cyberapps.commerce.order.Order"
provides="cyberapps.commerce.interfaces.IOrder"
trusted="True" />
<zope:class class="cyberapps.commerce.order.Order">
<require permission="zope.View"
interface="cyberapps.commerce.interfaces.IOrder" />
<require permission="zope.ManageContent"
set_schema="cyberapps.commerce.interfaces.IOrder" />
</zope:class>
<!--<zope:adapter factory="cyberapps.commerce.order.OrderItems" />-->
<zope:class class="cyberapps.commerce.order.OrderItem">
<require permission="zope.View"
interface="cybertools.commerce.interfaces.IOrderItem" />
<require permission="zope.ManageContent"
set_schema="cybertools.commerce.interfaces.IOrderItem" />
</zope:class>
<!-- setup -->
<zope:adapter factory="cyberapps.commerce.setup.SetupManager"
name="cyberapps.commerce" />
<include package=".browser" />
</configure>

View file

@ -0,0 +1,39 @@
# cyberapps.commerce.customer
""" Customer adapter and related classes.
"""
from zope.interface import implementer
from cyberapps.commerce.interfaces import ICustomer, IAddress
from cybertools.commerce.customer import Customer as BaseCustomer
from cybertools.commerce.customer import Address as BaseAddress
from loops.common import AdapterBase
from loops.common import ChildRelationSetProperty, ParentRelationSetProperty
from loops.interfaces import IConceptSchema
from loops.type import TypeInterfaceSourceList
TypeInterfaceSourceList.typeInterfaces += (ICustomer, IAddress)
@implementer(ICustomer)
class Customer(AdapterBase, BaseCustomer):
_adapterAttributes = ('context', '__parent__', 'shops')
_contextAttributes = list(ICustomer)
_noexportAttributes = ('shops', 'orders')
shops = ParentRelationSetProperty('shop.customer', noSecurityCheck=True)
orders = ChildRelationSetProperty('customer.order')
@property
def identifier(self):
return self.customerId
@implementer(IAddress)
class Address(AdapterBase, BaseAddress):
_contextAttributes = list(IAddress)

View file

@ -0,0 +1,90 @@
#
# Copyright (c) 2009 Helmut Merz helmutm@cy55.de
#
# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""
Specific interfaces.
$Id$
"""
from cyberapps.commerce.util import _
from cybertools.commerce.interfaces import IShop, IProduct, ICategory, IManufacturer
from cybertools.commerce.interfaces import ICustomer, IAddress
from cybertools.commerce.interfaces import IOrder, IOrderItem
from loops.interfaces import ILoopsAdapter
from loops.schema.base import Relation, RelationSet
class IShop(ILoopsAdapter, IShop):
pass
class ICategory(ILoopsAdapter, ICategory):
accessorySubcategories = RelationSet(
title=_(u'Accessory Subcategories'),
description=_(u'A collection of categories that contain products '
u'that may be used as accessories for this category.'),
target_types=('category',),
required=False)
selectedProducts = RelationSet(
title=_(u'Selected Products'),
description=_(u'Selected products for this category.'),
target_types=('product',),
required=False)
class IProduct(ILoopsAdapter, IProduct):
shops = RelationSet(
title=_(u'Shops'),
description=_(u'The shops providing this product..'),
target_types=('shop',),
required=False)
categories = RelationSet(
title=_(u'Categories'),
description=_(u'The product categories this product belongs to.'),
target_types=('category',),
required=False)
manufacturer = Relation(
title=_(u'Manufacturer'),
description=_(u'The manufacturer providing this product.'),
target_types=('manufacturer',),
required=False)
recommendedAccessories = RelationSet(
title=_(u'Recommended Accessories'),
description=_(u'Accessories for this product.'),
target_types=('product',),
required=False)
class ICustomer(ILoopsAdapter, ICustomer):
pass
class IAddress(ILoopsAdapter, IAddress):
pass
class IOrder(ILoopsAdapter, IOrder):
pass

View file

@ -0,0 +1,42 @@
# cyberapps.commerce.manager
""" The commerce manager (container, registry, ...).
"""
from zope.cachedescriptors.property import Lazy
from zope.component import adapts
from zope.interface import implementer
from cybertools.commerce.interfaces import IManager, IOrderItems
from loops.common import TypeInstancesProperty
from loops.concept import Concept
from loops.interfaces import IConceptManager
from loops.setup import addAndConfigureObject
@implementer(IManager)
class Manager(object):
adapts(IConceptManager)
langInfo = None
shops = TypeInstancesProperty('shop')
products = TypeInstancesProperty('product', 'productId', 'p')
categories = TypeInstancesProperty('category', 'name', 'cat')
customers = TypeInstancesProperty('customer', 'customerId', 'c',
container='customers')
orders = TypeInstancesProperty('order', 'orderId', '',
container='orders')
def __init__(self, context):
self.context = context
@Lazy
def records(self):
return self.context.getLoopsRoot().getRecordManager()
@Lazy
def orderItems(self):
return IOrderItems(self.records['orderitems'])

View file

@ -0,0 +1,59 @@
# cyberapps.commerce.order
""" Order and order item classes.
"""
from zope.app.intid.interfaces import IIntIds
from zope.cachedescriptors.property import Lazy
from zope import component
from zope.component import adapts
from zope.interface import implementer, Interface
from cyberapps.commerce.interfaces import IOrder
from cybertools.commerce.order import Order as BaseOrder
from cybertools.commerce.order import OrderItem as BaseOrderItem
from cybertools.commerce.order import OrderItems as BaseOrderItems
from loops.common import AdapterBase, ParentRelation
from loops.interfaces import ILoopsObject
from loops import util
from loops.type import TypeInterfaceSourceList
TypeInterfaceSourceList.typeInterfaces += (IOrder,)
@implementer(IOrder)
class Order(AdapterBase, BaseOrder):
_adapterAttributes = ('context', '__parent__', 'customer')
_contextAttributes = list(IOrder)
#shop = ParentRelation('shop.order') # implemented as context attribute
customer = ParentRelation('customer.order')
class OrderItem(BaseOrderItem):
def getObject(self, ref):
if isinstance(ref, int):
return util.getObjectForUid(ref)
if isinstance(ref, basestring):
if ref.isdigit:
return util.getObjectForUid(ref)
if ':' in ref:
tp, id = ref.split(':', 1)
return (tp, id)
return ref
class OrderItems(BaseOrderItems):
# utility methods
def getUid(self, obj):
if ILoopsObject.providedBy(obj):
return util.getUidForObject(obj, self.intIds)
elif isinstance(obj, AdapterBase):
return util.getUidForObject(obj.context, self.intIds)
return obj

View file

@ -0,0 +1,140 @@
# cyberapps.commerce.product
""" Product adapter.
"""
from zope.app.intid.interfaces import IIntIds
from zope import component
from zope.interface import implementer
from cyberapps.commerce.interfaces import IProduct, ICategory
from cybertools.commerce.interfaces import IManufacturer, ISupplier
from cybertools.commerce.product import Product as BaseProduct, Category as BaseCategory
from cybertools.commerce.product import Manufacturer, Supplier
from cybertools.util.cache import cache
from loops.common import adapted, AdapterBase, baseObject, ParentRelation
from loops.common import ParentRelationSetProperty, ChildRelationSetProperty
from loops.interfaces import IConceptSchema
from loops.type import TypeInterfaceSourceList
from loops import util
TypeInterfaceSourceList.typeInterfaces += (
IProduct, ICategory, IManufacturer, ISupplier)
@implementer(IProduct)
class Product(AdapterBase, BaseProduct):
_adapterAttributes = ('context', '__parent__',
'shops', 'manufacturer', 'suppliers', 'categories',
'recommendedAccessories')
_contextAttributes = list(IProduct)
_noexportAttributes = ('shops', 'categories', 'manufacturer', 'suppliers',
'recommendedAccessories')
shops = ParentRelationSetProperty('shop.product')
categories = ParentRelationSetProperty('category.product')
manufacturer = ParentRelation('manufacturer.product')
suppliers = ParentRelationSetProperty('supplier.product')
recommendedAccessories = ChildRelationSetProperty('product.accessory')
@property
def identifier(self):
return self.productId
@implementer(ICategory)
class Category(AdapterBase, BaseCategory):
_adapterAttributes = ('context', '__parent__',
'shops', 'subcategories', 'products',
'accessorySubcategories', 'selectedProducts')
_contextAttributes = list(ICategory)
_noexportAttributes = ('shops', 'products', 'parentCategories',
'subcategories', 'accessorySubcategories',
'selectedProducts')
shops = ParentRelationSetProperty('shop.category')
products = ChildRelationSetProperty('category.product')
parentCategories = ParentRelationSetProperty('standard', ICategory)
subcategories = ChildRelationSetProperty('standard')
accessorySubcategories = ChildRelationSetProperty('category.accessory')
selectedProducts = ChildRelationSetProperty('category.selected')
def getLongTitle(self):
parent = u''
defaultPredicate = self.getLoopsRoot().getConceptManager().getDefaultPredicate()
pr = self.context.getParentRelations([defaultPredicate])
for r in pr:
if r.relevance >= 0.8:
parent = r.first.title
if parent:
parent = u' - ' + parent
return self.title + parent
def getIsActiveCacheId(self, shop=None, *args, **kw):
shopId = shop is None and 'None' or shop.uid
filter = kw.get('filter') or ''
return 'commerce.category.isActive.%s.%s.%s' % (self.uid, shopId, filter)
@cache(getIsActiveCacheId, lifetime=93600)
def isActive(self, shop=None, filter=None):
depth = 0
for cat in self.subcategories:
if ICategory.providedBy(cat):
depth = max(depth, int(cat.isActive(shop, filter=filter)))
if depth:
return depth + 1
for prod in self.products:
if prod.isActive(shop, filter=filter):
return True
return False
def getAccessoryParents(self):
pred = self.getLoopsRoot().getConceptManager()['category.accessory']
for c in self.context.getParents([pred]):
yield adapted(c)
def getAllProductsCacheId(self, shop=None, *args, **kw):
shopId = shop is None and 'None' or shop.uid
filter = kw.get('filter') or ''
return 'commerce.category.allProducts.%s.%s.%s' % (self.uid, shopId, filter)
@cache(getAllProductsCacheId, lifetime=93600)
def getAllProductsUids(self, shop=None, filter=None):
intids = component.getUtility(IIntIds)
result = [util.getUidForObject(baseObject(p), intids)
for p in self.products
if p.isActive(shop, filter)]
for c in self.subcategories:
result.extend(c.getAllProductsUids())
return result
class Manufacturer(AdapterBase, Manufacturer):
_contextAttributes = list(IManufacturer)
_noexportAttributes = ('products',)
products = ChildRelationSetProperty('manufacturer.product')
def getIsActiveCacheId(self, shop, *args, **kw):
shopId = shop is None and 'None' or shop.uid
filter = kw.get('filter') or ''
return 'commerce.manufacturer.isActive.%s.%s.%s' % (self.uid, shopId, filter)
@cache(getIsActiveCacheId, lifetime=72000)
def isActive(self, shop=None, filter=None):
for prod in self.products:
if prod.isActive(shop, filter=filter):
return True
class Supplier(AdapterBase, Supplier):
_contextAttributes = list(ISupplier)
_noexportAttributes = ('products',)
products = ChildRelationSetProperty('supplier.product')

View file

@ -0,0 +1,58 @@
# cyberapps.commerce.setup
""" Automatic setup of a loops site for the commerce package.
"""
from zope.component import adapts
from cyberapps.commerce.order import OrderItem
from cybertools.commerce.interfaces import IShop, IProduct, ICategory
from cybertools.commerce.interfaces import ICustomer, IOrder
from cybertools.tracking.btree import TrackingStorage
from loops.concept import Concept, ConceptManager
from loops.interfaces import ITypeConcept
from loops.setup import SetupManager as BaseSetupManager
class SetupManager(BaseSetupManager):
def setup(self):
concepts = self.context.getConceptManager()
type = concepts.getTypeConcept()
predicate = concepts.getPredicateType()
customers = self.addObject(self.context, ConceptManager, 'customers')
orders = self.addObject(self.context, ConceptManager, 'orders')
# type concepts:
tShop = self.addAndConfigureObject(concepts, Concept, 'shop',
title=u'Shop', conceptType=type, typeInterface=IShop)
tProduct = self.addAndConfigureObject(concepts, Concept, 'product',
title=u'Product', conceptType=type, typeInterface=IProduct)
tCategory = self.addAndConfigureObject(concepts, Concept, 'category',
title=u'Category', conceptType=type, typeInterface=ICategory)
tCustomer = self.addAndConfigureObject(concepts, Concept, 'customer',
title=u'Customer', conceptType=type, typeInterface=ICustomer)
tOrder = self.addAndConfigureObject(concepts, Concept, 'order',
title=u'Order', conceptType=type, typeInterface=IOrder)
# predicates:
category_product = self.addObject(concepts, Concept, 'category.product',
title=u'category <- product', conceptType=predicate)
category_accessory = self.addObject(concepts, Concept, 'category.accessory',
title=u'category <- accessory subcategory', conceptType=predicate)
#category_selected = self.addObject(concepts, Concept, 'category.selected',
# title=u'category <- selected product', conceptType=predicate)
product_accessory = self.addObject(concepts, Concept, 'product.accessory',
title=u'product <- accessory', conceptType=predicate)
shop_product = self.addObject(concepts, Concept, 'shop.product',
title=u'shop <- product', conceptType=predicate)
shop_customer = self.addObject(concepts, Concept, 'shop.customer',
title=u'shop <- customer', conceptType=predicate)
#shop_order = self.addObject(concepts, Concept, 'shop.order',
# title=u'shop <- order', conceptType=predicate)
manufacturer_product = self.addObject(concepts, Concept, 'manufacturer.product',
title=u'manufacturer <- product', conceptType=predicate)
customer_order = self.addObject(concepts, Concept, 'customer.order',
title=u'customer <- order', conceptType=predicate)
# records:
records = self.context.getRecordManager()
if 'orderitems' not in records:
records['orderitems'] = TrackingStorage(trackFactory=OrderItem)

View file

@ -0,0 +1,31 @@
# cyberapps.commerce.shop
""" Shop adapter.
"""
from zope.interface import implementer
from zope.traversing.api import getName
from cyberapps.commerce.interfaces import IShop
from cybertools.commerce.shop import Shop as BaseShop
from loops.common import AdapterBase, ChildRelationSetProperty
from loops.type import TypeInterfaceSourceList
TypeInterfaceSourceList.typeInterfaces += (IShop,)
@implementer(IShop)
class Shop(AdapterBase, BaseShop):
_adapterAttributes = ('context', '__parent__',
'products', 'categories', 'suppliers', 'customers')
_contextAttributes = list(IShop)
_noexportAttributes = ('products', 'customers')
products = ChildRelationSetProperty('shop.product')
customers = ChildRelationSetProperty('shop.customer')
@property
def name(self):
return getName(self.context)

55
cyberapps/commerce/tests.py Executable file
View file

@ -0,0 +1,55 @@
# $Id$
import unittest, doctest
from zope.testing.doctestunit import DocFileSuite
from zope import component
from zope.interface.verify import verifyClass
from cyberapps.commerce.customer import Customer, Address
from cyberapps.commerce.interfaces import IShop, IProduct, ICategory
from cyberapps.commerce.interfaces import ICustomer, IAddress, IOrder
from cyberapps.commerce.order import Order, OrderItems
from cyberapps.commerce.product import Product, Category
from cyberapps.commerce.shop import Shop
from cyberapps.commerce.setup import SetupManager
from cybertools.commerce.interfaces import IProduct
from loops.interfaces import ILoops
from loops.setup import ISetupManager
from loops.tests.setup import TestSite as BaseTestSite
class TestSite(BaseTestSite):
def __init__(self, site):
self.site = site
def setup(self):
component.provideAdapter(SetupManager, (ILoops,), ISetupManager,
name='cyberapps.commerce')
component.provideAdapter(Shop, provides=IShop)
component.provideAdapter(Product, provides=IProduct)
component.provideAdapter(Category, provides=ICategory)
component.provideAdapter(Customer, provides=ICustomer)
component.provideAdapter(Address, provides=IAddress)
component.provideAdapter(Order, provides=IOrder)
component.provideAdapter(OrderItems)
concepts, resources, views = self.baseSetup()
return concepts, resources, views
class Test(unittest.TestCase):
"Basic tests for the cyberapps.commerce package."
def testSomething(self):
pass
def test_suite():
flags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
return unittest.TestSuite((
unittest.makeSuite(Test),
DocFileSuite('README.txt', optionflags=flags),
))
if __name__ == '__main__':
unittest.main(defaultTest='test_suite')

View file

@ -0,0 +1,23 @@
# cyberapps.commerce.util
""" Utility functions.
"""
from email.mime.text import MIMEText
from zope import component
from zope.i18nmessageid import MessageFactory
from zope.sendmail.interfaces import IMailDelivery
_ = MessageFactory('cyberapps.commerce')
def sendEMail(subject, message, sender, recipients):
if isinstance(message, unicode):
message = message.encode('UTF-8')
msg = MIMEText(message, 'plain', 'utf-8')
msg['Subject'] = subject
msg['From'] = sender
msg['To'] = ', '.join(recipients)
mailhost = component.getUtility(IMailDelivery, 'Mail')
mailhost.send(sender, recipients, msg.as_string())