def addtocart_ajax(self, skuCode=None, quantity=1, var1choice=None, var2choice=None): """ Add item to cart, return portlet HTML and translated status message """ translate = self.context.translate success = self.cart._add_item(skuCode, quantity, var1choice, var2choice) if success: status_msg_label = _(u'msg_label_info', default=u"Information") status_msg_text = _(u'msg_item_added', default=u"Added item to cart.") else: status_msg_label = _(u'msg_label_error', default=u"Error") status_msg_text = _( u'msg_item_disabled', default=u"Item is disabled and can't be added.") status_message = """\ <dt>%s</dt> <dd>%s</dd>""" % (translate(status_msg_label), translate(status_msg_text)) self.request.response.setHeader('Content-Type', 'application/json') return simplejson.dumps( dict(portlet_html=self.portlet_template(), status_message=status_message))
class DefaultStatusSet(object): implements(IStatusSet) adapts(Interface) # plone.registry.interfaces.IRecordsProxy # ftw.shop.interfaces.IShopConfiguration title = u"Default Status Set" vocabulary = SimpleVocabulary([ SimpleTerm( value=1, token=1, title=_(u'status_online_pending', default=u'Pending (online payment)'), ), SimpleTerm( value=2, token=2, title=_(u'status_confirmed', default=u'Confirmed (online payment)'), ), SimpleTerm( value=3, token=3, title=_(u'status_on_account', default=u'On account'), ) ]) def __init__(self, context): self.context = context pass
def add_item(self, skuCode=None, quantity=1, var1choice=None, var2choice=None): """Add item to cart and redirect to referer. The item must be identified by either its skuCode if it is an item without variations, or by its variation key. """ context = aq_inner(self.context) ptool = getToolByName(context, 'plone_utils') # Add item to cart success = self._add_item(skuCode, quantity, var1choice, var2choice) if success: ptool.addPortalMessage( _(u'msg_item_added', default=u'Added item to cart.'), 'info') else: ptool.addPortalMessage( _(u'msg_item_disabled', default=u'Item is disabled and can\'t be added.'), 'error') # redirect to referer referer = self.request.get('HTTP_REFERER', context.absolute_url()) if referer == 'localhost': referer = context.absolute_url() self.request.response.redirect(referer)
class ICartPortlet(IPortletDataProvider): """A portlet that displays the shopping cart """ mode = schema.Choice(title=_(u'Display mode'), description=_(u'Conditions under which portlet should be shown'), vocabulary=displayChoices, default='always' )
class AddForm(base.AddForm): form_fields = form.Fields(ICartPortlet) label = _(u"label_add_cart_portlet", default=u"Add Shopping Cart Portlet") description = _(u"help_add_cart_portlet", default=u"This portlet displays the shopping cart contents.") def create(self, data): assignment = Assignment(**data) return assignment
class DefaultPaymentProcessorChoiceStep(wizard.Step): implements(IPaymentProcessorChoiceStep) prefix = 'payment_processor_choice' label = _(u"label_default_payment_processor_choice_step", default="Payment Processor") title = _(u"title_default_payment_processor_step", default="Default Payment Processor Choice") description = _(u'help_default_payment_processor_choice_step', default=u"") fields = field.Fields(IDefaultPaymentProcessorChoice) fields['payment_processor'].widgetFactory = PaymentProcessorFieldWidget
class DefaultOrderReviewStep(wizard.Step): implements(IOrderReviewStep) prefix = 'order-review' label = _(u'label_order_review_step', default="Order Review") title = _(u"title_default_order_review_step", default="Default Order Review Step") description = _(u'help_order_review_step', default=u'') index = zvptf.ViewPageTemplateFile('templates/checkout/order_review.pt') def __init__(self, context, request, wiz): super(wizard.Step, self).__init__(context, request) self.wizard = wiz
def update_cart(self): """Update cart contents. """ context = aq_inner(self.context) ptool = getToolByName(context, 'plone_utils') # first delete items with quantity 0 del_items = [] # XXX - these are not skuCodes but item keys - rename! for skuCode in self.get_items().keys(): try: qty = int(float(self.request.get('quantity_%s' % skuCode))) if qty == 0: del_items.append(skuCode) except (ValueError, TypeError): ptool.addPortalMessage( _(u'msg_cart_invalidvalue', default=u"Invalid Values specified. Cart not updated."), 'error') referer = self.request.get('HTTP_REFERER', context.absolute_url()) self.request.response.redirect(referer) return for skuCode in del_items: self._remove_item(skuCode) # now update quantities (and VAT amounts, done by update_item) for skuCode, item in self.get_items().items(): quantity = float(self.request.get('quantity_%s' % skuCode)) dimensions = self.request.get('dimension_%s' % skuCode, []) if not isinstance(dimensions, list): dimensions = [dimensions] if not validate_dimensions(dimensions, item['selectable_dimensions']): raise ValueError('Invalid dimensions.') dimensions = map(Decimal, dimensions) if quantity <= 0: raise ValueError('Invalid quantity.') self.update_item(skuCode, quantity, dimensions) ptool.addPortalMessage( _(u'msg_cart_updated', default=u"Cart updated."), 'info') referer = self.request.get('HTTP_REFERER', context.absolute_url()) self.request.response.redirect(referer) return
def change_status(self): """Change status of several orders at once """ context = aq_inner(self.context) ptool = getToolByName(context, 'plone_utils') order_storage = self.getOrderStorage() orders = self.request.form.get('orders', []) new_status = self.request.form.get('form.widgets.status', None) # Change status of the orders for order_id in orders: try: order_id = int(order_id) except ValueError: # Invalid order_id - continue with next one continue order = order_storage.getOrder(order_id) order.status = int(new_status) msg = _(u'msg_status_changed', default=u"Changed status for ${no_orders} orders.", mapping={ u"no_orders" : len(orders)}) ptool.addPortalMessage(msg, 'info') return self.template()
def update_categories(self, categories): """Update the categories a ShopItem or ShopCategory belongs to """ #categories = self.request.get('categories', None) old_uids = [obj.UID() for obj in self.context.listCategories()] # add the checked categories new_uids = [uid for uid in categories if uid not in old_uids] for c in new_uids: self.context.addToCategory(c) # remove the unchecked categories uids_delete = [uid for uid in old_uids if uid not in categories] categories_delete = uids_delete for c in categories_delete: self.context.removeFromCategory(c) #update ranks for c in self.context.listCategories(): rank = self.context.REQUEST.get('rank_%s' % c.UID(), 1) self.context.setRankForCategory(c, rank) putils = getToolByName(self.context, 'plone_utils') putils.addPortalMessage(_(u'msg_categories_updated', default=u"Categories updated."), 'info') return self.context.REQUEST.RESPONSE.redirect('%s' % (self.context.absolute_url()))
class IOrderReviewSchema(Interface): """Fields for the order review step. """ comments = schema.Text( title=_(u'label_comments', default=u'Comments'), required=False)
def update_categories(self, categories): """Update the categories a ShopItem or ShopCategory belongs to """ #categories = self.request.get('categories', None) old_uids = [obj.UID() for obj in self.context.listCategories()] # add the checked categories new_uids = [uid for uid in categories if uid not in old_uids] for c in new_uids: self.context.addToCategory(c) # remove the unchecked categories uids_delete = [uid for uid in old_uids if uid not in categories] categories_delete = uids_delete for c in categories_delete: self.context.removeFromCategory(c) #update ranks for c in self.context.listCategories(): rank = self.context.REQUEST.get('rank_%s' % c.UID(), 1) self.context.setRankForCategory(c, rank) putils = getToolByName(self.context, 'plone_utils') putils.addPortalMessage( _(u'msg_categories_updated', default=u"Categories updated."), 'info') return self.context.REQUEST.RESPONSE.redirect( '%s' % (self.context.absolute_url()))
class IDefaultPaymentProcessorChoice(Interface): """Schema defining a customer choice from enabled payment processors """ payment_processor = schema.Choice( title=_(u"label_payment_processor", default="Payment Processor"), vocabulary="ftw.shop.enabled_payment_processors", required=True)
class IShippingAddress(Interface): """Schema defining a form for entering a shipping address """ used = schema.Bool( title=_(u'label_used', default=u'Different from invoice address'), default=False, required=False)
def addtocart_ajax(self, skuCode=None, quantity=1, var1choice=None, var2choice=None, dimensions=None): """ Add item to cart, return portlet HTML and translated status message """ translate = self.context.translate # unpack dimensions from request dimensions = dimensions.split('|') if dimensions else [] if validate_dimensions(dimensions, self.context.getSelectableDimensions()): dimensions = map(Decimal, dimensions) success = self.cart._add_item(skuCode, quantity, var1choice, var2choice, dimensions) if success: status_msg_label = _(u'msg_label_info', default=u"Information") status_msg_text = _(u'msg_item_added', default=u"Added item to cart.") else: status_msg_label = _(u'msg_label_error', default=u"Error") status_msg_text = _(u'msg_item_disabled', default=u"Item is disabled and can't be added.") else: status_msg_label = _(u'msg_label_error', default=u"Error") status_msg_text = _(u'msg_invalid_dimensions', default=u"Invalid dimensions.") status_message = """\ <dt>%s</dt> <dd>%s</dd>""" % (translate(status_msg_label), translate(status_msg_text)) self.request.response.setHeader('Content-Type', 'application/json') return simplejson.dumps(dict(portlet_html=self.portlet_template(), status_message=status_message))
def purge_cart(self): """Remove all items from cart. """ session = self.request.SESSION session[CART_KEY] = {} context = aq_inner(self.context) ptool = getToolByName(context, 'plone_utils') ptool.addPortalMessage( _(u'msg_cart_emptied', default=u"Cart emptied."), 'info') referer = self.request.get('HTTP_REFERER', context.absolute_url()) self.request.response.redirect(referer) return
def purge_cart(self): """Remove all items from cart. """ session = self.request.SESSION session[CART_KEY] = {} context = aq_inner(self.context) ptool = getToolByName(context, 'plone_utils') ptool.addPortalMessage(_(u'msg_cart_emptied', default=u"Cart emptied."), 'info') referer = self.request.get('HTTP_REFERER', context.absolute_url()) self.request.response.redirect(referer) return
class DefaultShippingAddressStep(wizard.Step): implements(IShippingAddressStep) prefix = 'shipping_address' label = _(u"label_default_shipping_address_step", default="Shipping Address") title = _(u"title_default_shipping_address_step", default="Default Shipping Address") description = _(u'help_default_shipping_address_step', default=u"") prefill_fields = [ 'title', 'firstname', 'lastname', 'company', 'street1', 'street2', 'zipcode', 'city' ] fields = field.Fields(IShippingAddress) + \ field.Fields(IDefaultContactInformation).select( 'title', 'firstname', 'lastname', 'company', 'street1', 'street2', 'zipcode', 'city') def __init__(self, context, request, wiz): super(wizard.Step, self).__init__(context, request) self.wizard = wiz def updateWidgets(self): super(DefaultShippingAddressStep, self).updateWidgets() if 'contact_information' in self.wizard.session and \ (not 'shipping_address' in self.wizard.session \ or self.wizard.session['shipping_address'] == {}): # Prefill shipping address with data from contact_info (prev step) contact_info = self.wizard.session['contact_information'] for fieldname in self.prefill_fields: try: self.widgets[fieldname].value = contact_info[fieldname] except KeyError: pass
def addtocart_ajax(self, skuCode=None, quantity=1, var1choice=None, var2choice=None): """ Add item to cart, return portlet HTML and translated status message """ translate = self.context.translate success = self.cart._add_item(skuCode, quantity, var1choice, var2choice) if success: status_msg_label = _(u'msg_label_info', default=u"Information") status_msg_text = _(u'msg_item_added', default=u"Added item to cart.") else: status_msg_label = _(u'msg_label_error', default=u"Error") status_msg_text = _(u'msg_item_disabled', default=u"Item is disabled and can't be added.") status_message = """\ <dt>%s</dt> <dd>%s</dd>""" % (translate(status_msg_label), translate(status_msg_text)) self.request.response.setHeader('Content-Type', 'application/json') return simplejson.dumps(dict(portlet_html=self.portlet_template(), status_message=status_message))
def update_cart(self): """Update cart contents. """ context = aq_inner(self.context) ptool = getToolByName(context, 'plone_utils') # first delete items with quantity 0 del_items = [] # XXX - these are not skuCodes but item keys - rename! for skuCode in self.get_items().keys(): try: qty = int(float(self.request.get('quantity_%s' % skuCode))) if qty == 0: del_items.append(skuCode) except (ValueError, TypeError): ptool.addPortalMessage( _(u'msg_cart_invalidvalue', default=u"Invalid Values specified. Cart not updated."), 'error') referer = self.request.get('HTTP_REFERER', context.absolute_url()) self.request.response.redirect(referer) return for skuCode in del_items: self._remove_item(skuCode) # now update quantities (and VAT amounts, done by update_item) for skuCode, item in self.get_items().items(): quantity = int(float(self.request.get('quantity_%s' % skuCode))) if quantity != item['quantity'] and quantity != 0: self.update_item(skuCode, quantity) ptool.addPortalMessage( _(u'msg_cart_updated', default=u"Cart updated."), 'info') referer = self.request.get('HTTP_REFERER', context.absolute_url()) self.request.response.redirect(referer) return
def update_cart(self): """Update cart contents. """ context = aq_inner(self.context) ptool = getToolByName(context, 'plone_utils') # first delete items with quantity 0 del_items = [] # XXX - these are not skuCodes but item keys - rename! for skuCode in self.get_items().keys(): try: qty = int(float(self.request.get('quantity_%s' % skuCode))) if qty == 0: del_items.append(skuCode) except (ValueError, TypeError): ptool.addPortalMessage( _(u'msg_cart_invalidvalue', default=u"Invalid Values specified. Cart not updated."), 'error') referer = self.request.get('HTTP_REFERER', context.absolute_url()) self.request.response.redirect(referer) return for skuCode in del_items: self._remove_item(skuCode) # now update quantities (and VAT amounts, done by update_item) for skuCode, item in self.get_items().items(): quantity = int(float(self.request.get('quantity_%s' % skuCode))) if quantity != item['quantity'] and quantity != 0: self.update_item(skuCode, quantity) ptool.addPortalMessage(_(u'msg_cart_updated', default=u"Cart updated."), 'info') referer = self.request.get('HTTP_REFERER', context.absolute_url()) self.request.response.redirect(referer) return
def add_item(self, skuCode=None, quantity=1, var1choice=None, var2choice=None): """Add item to cart and redirect to referer. The item must be identified by either its skuCode if it is an item without variations, or by its variation key. """ context = aq_inner(self.context) ptool = getToolByName(context, 'plone_utils') # Add item to cart success = self._add_item(skuCode, quantity, var1choice, var2choice) if success: ptool.addPortalMessage(_(u'msg_item_added', default=u'Added item to cart.'), 'info') else: ptool.addPortalMessage(_(u'msg_item_disabled', default=u'Item is disabled and can\'t be added.'), 'error') # redirect to referer referer = self.request.get('HTTP_REFERER', context.absolute_url()) if referer == 'localhost': referer = context.absolute_url() self.request.response.redirect(referer)
def remove_item(self): """Remove an item from cart. """ context = aq_inner(self.context) ptool = getToolByName(context, 'plone_utils') # remove_item doesn't expect skuCode but item keys - rename! skuCode = self.request.get('skuCode') if skuCode: self._remove_item(skuCode) ptool.addPortalMessage( _(u'msg_cart_updated', default=u"Cart updated."), 'info') referer = self.request.get('HTTP_REFERER', context.absolute_url()) self.request.response.redirect(referer) return
def remove_item(self): """Remove an item from cart. """ context = aq_inner(self.context) ptool = getToolByName(context, 'plone_utils') # remove_item doesn't expect skuCode but item keys - rename! skuCode = self.request.get('skuCode') if skuCode: self._remove_item(skuCode) ptool.addPortalMessage(_(u'msg_cart_updated', default=u"Cart updated."), 'info') referer = self.request.get('HTTP_REFERER', context.absolute_url()) self.request.response.redirect(referer) return
def SuppliersVocabulary(context): """Returns a vocabulary of the available suppliers """ # context is the portal config options, whose context is the portal catalog = getToolByName(context, 'portal_catalog') suppliers = catalog(portal_type="Supplier") items = [(brain.UID, brain.Title) for brain in suppliers] terms = [SimpleTerm(value=pair[0], token=pair[0], title=pair[1]) for pair in items] terms.insert(0, SimpleTerm(value='', token='none', title=_("label_supplier_from_parent", default="Supplier from parent category"))) directlyProvides(SuppliersVocabulary, IVocabularyFactory) return vocabulary.SimpleVocabulary(terms)
def __call__(self): """ Self-submitting form that lets the user edit categories that ShopItems or ShopCategories belong to """ form = self.request.form # Make sure we had a proper form submit, not just a GET request submitted = form.get('form.submitted', False) if submitted: edited_category_data = self.request.get('categories', []) self.update_categories(edited_category_data) IStatusMessage(self.request).addStatusMessage(_( u'msg_categories_updated', default=u"Categories updated."), type="info") self.request.RESPONSE.redirect(self.context.absolute_url()) return self.template()
def __call__(self): """ Self-submitting form that lets the user edit categories that ShopItems or ShopCategories belong to """ form = self.request.form # Make sure we had a proper form submit, not just a GET request submitted = form.get('form.submitted', False) if submitted: edited_category_data = self.request.get('categories', []) self.update_categories(edited_category_data) IStatusMessage(self.request).addStatusMessage( _(u'msg_categories_updated', default=u"Categories updated."), type="info") self.request.RESPONSE.redirect(self.context.absolute_url()) return self.template()
class ShopConfigurationForm(RegistryEditForm, group.GroupForm): """Configuration form for the ftw.shop configlet """ schema = IShopConfiguration fields = field.Fields(IShopConfiguration).omit( 'shop_email', 'mail_bcc', 'mail_subject', 'always_notify_shop_owner', 'payment_processor_step_group', 'enabled_payment_processors', 'contact_info_step_group', 'shipping_address_step_group', 'order_review_step_group', 'phone_number', 'vat_enabled', 'vat_number', 'vat_rates') groups = (MailGroup, CheckoutGroup, VATGroup) label = _(u'label_shop_configuration', default=u"Shop configuration") def updateFields(self): super(ShopConfigurationForm, self).updateFields() def updateWidgets(self): super(ShopConfigurationForm, self).updateWidgets()
def cancel_orders(self): """Cancel orders by their order_ids """ context = aq_inner(self.context) ptool = getToolByName(context, 'plone_utils') order_storage = self.getOrderStorage() orders = self.request.form.get('orders', []) for order_id in orders: try: order_id = int(order_id) except ValueError: # Invalid order_id - continue with next one continue order_storage.cancelOrder(order_id) msg = _(u'msg_order_cancelled', default=u"${no_orders} orders cancelled.", mapping={ u"no_orders" : len(orders)}) ptool.addPortalMessage(msg, 'info') self.request.response.redirect("%s/order_manager" % context.absolute_url())
def SuppliersVocabulary(context): """Returns a vocabulary of the available suppliers """ # context is the portal config options, whose context is the portal catalog = getToolByName(context, 'portal_catalog') suppliers = catalog(portal_type="Supplier") items = [(brain.UID, brain.Title) for brain in suppliers] terms = [ SimpleTerm(value=pair[0], token=pair[0], title=pair[1]) for pair in items ] terms.insert( 0, SimpleTerm(value='', token='none', title=_("label_supplier_from_parent", default="Supplier from parent category"))) directlyProvides(SuppliersVocabulary, IVocabularyFactory) return vocabulary.SimpleVocabulary(terms)
def addtocart_ajax(self, skuCode=None, quantity=1, var1choice=None, var2choice=None, dimensions=None): """ Add item to cart, return portlet HTML and translated status message """ translate = self.context.translate # unpack dimensions from request dimensions = dimensions.split('|') if dimensions else [] if validate_dimensions(dimensions, self.context.getSelectableDimensions()): dimensions = map(Decimal, dimensions) success = self.cart._add_item(skuCode, quantity, var1choice, var2choice, dimensions) if success: status_msg_label = _(u'msg_label_info', default=u"Information") status_msg_text = _(u'msg_item_added', default=u"Added item to cart.") else: status_msg_label = _(u'msg_label_error', default=u"Error") status_msg_text = _( u'msg_item_disabled', default=u"Item is disabled and can't be added.") else: status_msg_label = _(u'msg_label_error', default=u"Error") status_msg_text = _(u'msg_invalid_dimensions', default=u"Invalid dimensions.") status_message = """\ <dt>%s</dt> <dd>%s</dd>""" % (translate(status_msg_label), translate(status_msg_text)) self.request.response.setHeader('Content-Type', 'application/json') return simplejson.dumps( dict(portlet_html=self.portlet_template(), status_message=status_message))
def __call__(self): portal_url = getToolByName(self.context, "portal_url") ptool = getToolByName(self.context, "plone_utils") portal = portal_url.getPortalObject() shop = getattr(portal, "shop", None) if not shop: portal.invokeFactory("Folder", "shop", title="Shop") shop = portal.shop # Add Shopping Cart portlet to Shop Root column = getUtility(IPortletManager, name=u"plone.rightcolumn", context=shop) manager = getMultiAdapter((shop, column), IPortletAssignmentMapping) if "ftw.shop.portlets.cart" not in manager.keys(): manager["ftw.shop.portlets.cart"] = portlets.cart.Assignment() ptool.addPortalMessage(_(u"msg_shop_initialized", default=u"Shop structure initialized."), "info") # redirect to referer referer = self.request.get("HTTP_REFERER", portal.absolute_url()) if not referer in ["", "localhost"]: self.request.response.redirect(referer) else: self.request.response.redirect(portal.absolute_url()) return
def add_level(self): fields = ['active', 'skuCode', 'price', 'description'] request = getRequest() pps = getMultiAdapter((self.context, request), name='plone_portal_state') language = pps.language() if len(self.getVariationAttributes()) == 1: new_value_1 = translate(_('label_new_value_1', default=u'New value 1'), domain='ftw.shop', context=self.context, target_language=language) new_value_2 = translate(_('label_new_value_2', default=u'New value 2'), domain='ftw.shop', context=self.context, target_language=language) new_attr = translate(_('label_new_attr', default=u'New attribute'), domain='ftw.shop', context=self.context, target_language=language) self.context.getField('variation2_values').set(self.context, [new_value_1, new_value_2]) self.context.getField('variation2_attribute').set(self.context, new_attr) # Initialize var data for newly added level with default values for i in range(len(self.getVariation1Values())): for j in range(len(self.getVariation2Values())): vardata = {} for f in fields: vcode = "var-%s-%s" % (i, j) data = self.getVariationData(i, j, f) vardata[f] = data partial_vardict = {vcode: vardata} self.updateVariationConfig(partial_vardict) elif len(self.getVariationAttributes()) == 0: new_value_1 = translate(_('label_new_value_1', default=u'New value 1'), domain='ftw.shop', context=self.context, target_language=language) new_value_2 = translate(_('label_new_value_2', default=u'New value 2'), domain='ftw.shop', context=self.context, target_language=language) new_attr = translate(_('label_new_attr', default=u'New attribute'), domain='ftw.shop', context=self.context, target_language=language) self.context.getField('variation1_values').set(self.context, [new_value_1, new_value_2]) self.context.getField('variation1_attribute').set(self.context, new_attr) # Initialize var data for newly added level with default values for i in range(len(self.getVariation1Values())): vardata = {} for f in fields: vcode = "var-%s" % (i) data = self.getVariationData(i, None, f) vardata[f] = data partial_vardict = {vcode: vardata} self.updateVariationConfig(partial_vardict)
from ftw.shop import shopMessageFactory as _ from ftw.shop.interfaces import IShopCategory from ftw.shop.config import PROJECTNAME from ftw.shop.content.categorizeable import Categorizeable ShopCategorySchema = ATFolderSchema.copy() + atapi.Schema(( atapi.ReferenceField( 'supplier', required = 0, languageIndependent=True, relationship = 'item_supplier', vocabulary_factory="ftw.shop.suppliers_vocabulary", widget = atapi.ReferenceWidget( label = _(u"label_supplier", default=u"Supplier"), description = _(u"desc_supplier", default=u""), checkbox_bound = 10, ), ), )) class ShopCategory(Categorizeable, ATFolder): """A category for shop items""" implements(IShopCategory) meta_type = "ShopCategory" schema = ShopCategorySchema def fullTitle(self):
def validate(self, value): super(EmailAddressValidator, self).validate(value) if not value or not is_email_valid(value): raise Invalid(_(u'This email address is invalid.'))
class DefaultContactInfoStep(wizard.Step): implements(IContactInformationStep) prefix = 'contact_information' label = _(u"label_default_contact_info_step", default="Contact Information") title = _(u"title_default_contact_info_step", default="Default Contact Information") description = _(u'help_default_contact_info_step', default=u"") fields = field.Fields(IDefaultContactInformation) prefill_fields = [ 'title', 'firstname', 'lastname', 'email', 'company', 'street1', 'street2', 'phone', 'zipcode', 'city', 'country' ] def __init__(self, context, request, wiz): super(wizard.Step, self).__init__(context, request) self.wizard = wiz def updateWidgets(self): super(DefaultContactInfoStep, self).updateWidgets() request = self.context.REQUEST # if user is authenticated: # prefill form from portal_memberdata mt = getToolByName(self.context, 'portal_membership') if not mt.isAnonymousUser(): member = mt.getAuthenticatedMember() fullname = member.getProperty('fullname', '') if not isinstance(fullname, unicode): # assume it's uft-8 fullname = fullname.decode('utf-8') if fullname.find(' ') > -1: firstname, lastname = fullname.rsplit(' ', 1) else: firstname = lastname = '' email = member.getProperty('email') self.widgets['firstname'].value = firstname self.widgets['lastname'].value = lastname self.widgets['email'].value = unicode(email) if COOKIE_ADDRESS_KEY in request: # Prefill contact data with values from cookie cookie_data = simplejson.loads( base64.b64decode(request[COOKIE_ADDRESS_KEY])) for key in cookie_data.keys(): if isinstance(cookie_data[key], basestring): cookie_data[key] = unicode(cookie_data[key]) for fieldname in self.prefill_fields: try: self.widgets[fieldname].value = cookie_data[fieldname] except KeyError: pass elif SESSION_ADDRESS_KEY in request.SESSION.keys(): # Prefill contact data form with values from session contact_info = request.SESSION[SESSION_ADDRESS_KEY] for fieldname in self.prefill_fields: try: self.widgets[fieldname].value = contact_info[fieldname] except KeyError: pass self.widgets['zipcode'].size = 5
def title(self): """This property is used to give the title of the portlet in the "manage portlets" screen. """ return _(u"label_cart_portlet", default=u"Cart Portlet")
from ftw.shop import shopMessageFactory as _ from ftw.shop.interfaces import IShopItem from plone.app.portlets.portlets import base from plone.portlets.interfaces import IPortletDataProvider from zope import schema from zope.component import getMultiAdapter from zope.formlib import form from zope.interface import implements from zope.schema.vocabulary import SimpleTerm from zope.schema.vocabulary import SimpleVocabulary displayChoices = SimpleVocabulary( [SimpleTerm(value='always', token='always', title=_(u'label_always', default=u'Always'), ), SimpleTerm(value='only_if_items', token='only_if_items', title=_(u'label_only_if_items', default=u'Only if ShopItems available in current folder'), ) ] ) class ICartPortlet(IPortletDataProvider): """A portlet that displays the shopping cart """ mode = schema.Choice(title=_(u'Display mode'), description=_(u'Conditions under which portlet should be shown'),
class CheckoutWizard(wizard.Wizard): label = _(u"label_checkout_wizard", default="Checkout") index = zvptf.ViewPageTemplateFile('templates/checkout-wizard.pt') def __init__(self, context, request): super(CheckoutWizard, self).__init__(context, request) self.context = context self.request = request def getSelectedPaymentProcessor(self): payment_processor = None if 'payment_processor_choice' not in self.session.keys(): return None try: pp_name = self.session.get('payment_processor_choice').get( 'payment_processor') except KeyError: try: pp_name = self.request.SESSION.get( 'payment_processor_choice').get('payment_processor') except KeyError: # No payment processor step activated pp_name = "none" for name, adapter in getAdapters( (self.context, self.request, self.context), IPaymentProcessor): if name == pp_name: payment_processor = adapter return payment_processor @property def steps(self): contact_info_steps = () shipping_address_steps = () contact_info_step_groups = getAdapters( (self.context, self.request, self), IContactInformationStepGroup) shipping_address_step_groups = getAdapters( (self.context, self.request, self), IShippingAddressStepGroup) payment_processor_step_groups = getAdapters( (self.context, self.request, self), IPaymentProcessorStepGroup) order_review_step_groups = getAdapters( (self.context, self.request, self), IOrderReviewStepGroup) registry = getUtility(IRegistry) shop_config = registry.forInterface(IShopConfiguration) # Get steps for selected Contact Info Step Group selected_contact_info_step_group = shop_config.contact_info_step_group for name, step_group_adapter in contact_info_step_groups: if name == selected_contact_info_step_group: contact_info_steps = step_group_adapter.steps # Get steps for selected Shipping Address Step Group selected_shipping_address_step_group = shop_config.shipping_address_step_group for name, step_group_adapter in shipping_address_step_groups: if name == selected_shipping_address_step_group: shipping_address_steps = step_group_adapter.steps # Get steps for selected Payment Processor Step Group selected_pp_step_group = shop_config.payment_processor_step_group for name, step_group_adapter in payment_processor_step_groups: if name == selected_pp_step_group: payment_processor_steps = step_group_adapter.steps # Get steps for selected Order Review Step Group selected_order_review_step_group = shop_config.order_review_step_group for name, step_group_adapter in order_review_step_groups: if name == selected_order_review_step_group: order_review_steps = step_group_adapter.steps return contact_info_steps + \ shipping_address_steps + \ payment_processor_steps + \ order_review_steps @button.buttonAndHandler(_(u'btn_back', default="Back"), name='back', condition=lambda form: not form.onFirstStep) def handleBack(self, action): data, errors = self.currentStep.extractData() if errors: errorMessage = '<ul>' for error in errors: if errorMessage.find(error.message): errorMessage += '<li>' + error.message + '</li>' errorMessage += '</ul>' self.status = errorMessage else: self.currentStep.applyChanges(data) self.updateCurrentStep(self.currentIndex - 1) # Back can change the conditions for the finish button, # so we need to reconstruct the button actions, since we # do not redirect. self.updateActions() @button.buttonAndHandler(_(u'btn_continue', default='Next'), name='continue', condition=lambda form: not form.onLastStep) def handleContinue(self, action): data, errors = self.currentStep.extractData() if errors: errorMessage = '<ul>' for error in errors: if errorMessage.find(error.message): errorMessage += '<li>' + error.message + '</li>' errorMessage += '</ul>' self.status = errorMessage else: self.currentStep.applyChanges(data) self.updateCurrentStep(self.currentIndex + 1) # Proceed can change the conditions for the finish button, # so we need to reconstruct the button actions, since we # do not redirect. self.updateActions() @button.buttonAndHandler( _(u'btn_finish', default='Finish'), name='finish', condition=lambda form: form.allStepsFinished or form.onLastStep) def handleFinish(self, action): data, errors = self.currentStep.extractData() if errors: self.status = self.formErrorsMessage return else: self.status = self.successMessage self.finished = True self.currentStep.applyChanges(data) self.finish() address_data = {} address_data.update(self.session['contact_information']) self.request.SESSION[SESSION_ADDRESS_KEY] = address_data shipping_data = {} shipping_data.update(self.session['shipping_address']) self.request.SESSION[SESSION_SHIPPING_KEY] = shipping_data self.request.SESSION['order_confirmation'] = True payment_processor_choice = {} if 'payment_processor_choice' in self.session.keys(): payment_processor_choice.update( self.session['payment_processor_choice']) self.request.SESSION[ 'payment_processor_choice'] = payment_processor_choice # Save contact information in a cookie in order to prefill # form if customer returns cookie_value = base64.b64encode( simplejson.dumps(self.session['contact_information'])) expiry_date = ( datetime.now() + timedelta(days=90)).strftime("%a, %d-%b-%Y %H:%M:%S GMT") self.request.RESPONSE.setCookie(COOKIE_ADDRESS_KEY, cookie_value, path='/', expires=expiry_date) self.request.SESSION[self.sessionKey] = {} self.sync() pp = self.getSelectedPaymentProcessor() if pp is not None and pp.external: self.request.SESSION['external-processor-url'] = pp.url self.request.SESSION[ 'external-processor-url'] = "http://localhost:8077/" self.request.response.redirect('checkout')
from Products.Archetypes.atapi import registerType from ftw.shop.interfaces import IShopItem, IBuyable from ftw.shop.content.categorizeable import Categorizeable from ftw.shop.config import PROJECTNAME from ftw.shop import shopMessageFactory as _ ShopItemSchema = ATContentTypeSchema.copy() + atapi.Schema(( atapi.ImageField( 'image', required=False, languageIndependent=True, widget = atapi.ImageWidget( label = _(u"label_image", default=u"Image"), ), storage=AnnotationStorage(), sizes= {'large': (768, 768), 'mini': (200, 200), 'thumb': (128, 128), }, ), atapi.TextField( 'text', required=False, searchable=True, primary=True, storage=atapi.AnnotationStorage(migrate=True),
def download_csv(self): """Returns a CSV file containing the shop orders """ filename = "orders.csv" stream = cStringIO.StringIO() csv_writer = csv.writer(stream, dialect='excel', delimiter=';', quotechar='"', quoting=csv.QUOTE_MINIMAL) core_cols = ['order_id', 'title', 'status', 'total', 'date', 'customer_title', 'customer_firstname', 'customer_lastname', 'customer_company', 'customer_email', 'customer_street1', 'customer_street2', 'customer_phone', 'customer_zipcode', 'customer_city', 'customer_shipping_address', 'customer_country', 'customer_comments'] # Create union of core_cols + all_cols to retain order all_cols = self.order_storage.getFieldNames() columns = core_cols + filter(lambda x:x not in core_cols, all_cols) cart_cols = ['sku_code', 'quantity', 'dimensions', 'title', 'price', 'item_total', 'supplier_name', 'supplier_email'] column_titles = [COLUMN_TITLES[col].decode('utf-8').encode('cp1252') for col in columns + cart_cols] # Write header row csv_writer.writerow(column_titles) for order in self.order_results: order_data = [getattr(order, attr, '') for attr in columns] # Get the total via getTotal accessor to convert it to Decimal order_data[columns.index('total')] = order.getTotal() for i, value in enumerate(order_data): if isinstance(value, unicode): order_data[i] = value.encode('cp1252') for cart_item in order.cartitems: # format dimensions dimensions = [(cart_item.dimensions[i], dim) for i, dim in enumerate(cart_item.selectable_dimensions)] dim_strings = [' '.join([str(dim[0]), translate(_(dim[1]), context=self.request)]) for dim in dimensions] dim_formatted = ','.join(dim_strings) cart_data = [cart_item.sku_code, cart_item.quantity, dim_formatted, cart_item.title, cart_item.getPrice(), cart_item.getTotal(), cart_item.supplier_name, cart_item.supplier_email] for i, value in enumerate(cart_data): if isinstance(value, unicode): cart_data[i] = value.encode('cp1252') csv_writer.writerow(order_data + cart_data) RESPONSE = self.request.RESPONSE header_value = contentDispositionHeader('attachment', 'cp1252', filename=filename) if not DEBUG: RESPONSE.setHeader("Content-Disposition", header_value) RESPONSE.setHeader("Content-Type", 'text/comma-separated-values;charset=%s' % 'cp1252') else: RESPONSE.setHeader("Content-Type", 'text/plain; charset=%s' % 'cp1252') stream.seek(0) return stream.read()
class DefaultShippingAddressStepGroup(BaseStepGroup): implements(IShippingAddressStepGroup) adapts(Interface, Interface, Interface) title = _(u"title_default_shipping_address_step_group", default="Default Shipping Address") steps = (DefaultShippingAddressStep, )
class VATGroup(group.Group): label = _('label_vat_group', default=u'VAT') fields = field.Fields(IShopConfiguration).select('vat_enabled', 'vat_number', 'vat_rates')
def checkout(self): """Process checkout """ context = aq_inner(self.context) session = context.REQUEST.SESSION ptool = getToolByName(context, 'plone_utils') url = context.absolute_url() # check if we have something in the cart items = self.cart_items() if not items: ptool.addPortalMessage(_(u'msg_no_cart', default=u"Can't proceed with empty cart."), 'error') self.request.response.redirect(url) navroot = api.portal.get_navigation_root(context) omanager = getMultiAdapter((navroot, self.request), name=u'order_manager') # Check we got all the data we need from the wizard try: order_id = omanager.addOrder() except MissingCustomerInformation: self.request.response.redirect('%s/checkout-wizard' % url) return except MissingShippingAddress: self.request.response.redirect('%s/checkout-wizard' % url) return except MissingOrderConfirmation: self.request.response.redirect('%s/checkout-wizard' % url) return except MissingPaymentProcessor: self.request.response.redirect('%s/checkout-wizard' % url) return # Get the payment processor selected by the customer payment_processor_name = session.get( 'payment_processor_choice').get('payment_processor') for name, adapter in getAdapters((context, context.REQUEST, context), IPaymentProcessor): if name == payment_processor_name: payment_processor = adapter if not payment_processor_name or not payment_processor.external: # No payment processor step at all OR payment by invoice customer_info = self.request.SESSION[SESSION_ADDRESS_KEY] self.request.SESSION.invalidate() # Get a new session object, since the old one still returns # stale data even though it has been invalidated session = context.session_data_manager.getSessionData() session[SESSION_ADDRESS_KEY] = customer_info # Set correct status for payment by invoice order = omanager.getOrder(order_id) order.status = ONACCOUNT_KEY omanager.sendOrderMails(order_id) self.request.response.redirect( '%s/thankyou?order_id=%s' % (url, order.title)) return else: #self.request.SESSION.invalidate() self.request.SESSION['order_id'] = order_id pp_launch_page = 'external-payment-processor' pp_launch_page = payment_processor.launch_page self.request.response.redirect('%s/%s' % (url, pp_launch_page)) return
from zope.interface import implements from Products.ATContentTypes.config import HAS_LINGUA_PLONE if HAS_LINGUA_PLONE: from Products.LinguaPlone.public import registerType else: from Products.Archetypes.atapi import registerType SupplierSchema = ATContentTypeSchema.copy() + atapi.Schema(( atapi.TextField( name='email', required=True, label=_(u'label_supplier_email', default=u"E-Mail")), atapi.TextField( name='address', required=False, allowable_content_types=('text/plain', ), default_content_type='text/plain', default_input_type='text/plain', default_output_type='text/x-html-safe', widget=atapi.RichWidget( label=_(u'label_supplier_address', default=u"Address"), description=_(u'help_supplier_address', default=u"Supplier's postal address")))))
if HAS_LINGUA_PLONE: from Products.LinguaPlone.public import registerType else: from Products.Archetypes.atapi import registerType from ftw.shop.interfaces import IBuyable from ftw.shop.interfaces import IShopItem from ftw.shop.interfaces import IVariationConfig from ftw.shop.content.categorizeable import Categorizeable from ftw.shop.config import PROJECTNAME from ftw.shop import shopMessageFactory as _ selectable_dimensions = { 'no_dimensions': { 'label': _(u"label_no_dimensions", default=u"---"), 'dimension_unit': None, 'dimensions': [] }, 'length_mm_mm': { 'label': _(u"label_length", default=u"Length"), 'dimension_unit': u'mm', 'dimensions': [_(u"Length (mm)")] }, 'length_m_m': { 'label': _(u"label_length", default=u"Length"), 'dimension_unit': u'm', 'dimensions': [_(u"Length (m)")] }, 'length_width_mm_mm2': { 'label': _(u"label_l_w", default=u"Length, Width"),
class MailGroup(group.Group): label = _('label_mail_group', default=u'Mail') fields = field.Fields(IShopConfiguration).select( 'shop_email', 'mail_bcc', 'mail_subject', 'always_notify_shop_owner')
class DefaultOrderReviewStepGroup(BaseStepGroup): implements(IOrderReviewStepGroup) adapts(Interface, Interface, Interface) title = _(u"title_default_order_review_step_group", default="Default Order Review Step Group") steps = (DefaultOrderReviewStep, )
class CheckoutGroup(group.Group): label = _('label_checkout_group', default=u'Checkout') fields = field.Fields(IShopConfiguration).select( 'payment_processor_step_group', 'enabled_payment_processors', 'contact_info_step_group', 'shipping_address_step_group', 'order_review_step_group', 'phone_number')
def __call__(self): """ Self-submitting form that displays ShopItem Variations and updates them """ form = self.request.form if form.get('remove_level'): variation_config = IVariationConfig(self.context) variation_config.remove_level() if form.get('reduce_level'): variation_config = IVariationConfig(self.context) variation_config.reduce_level() if form.get('add_level'): variation_config = IVariationConfig(self.context) variation_config.add_level() if form.get('addvalue'): fn = None idx_and_pos = form.get('addvalue') idx, pos = idx_and_pos.split('-') idx = int(idx) pos = int(pos) + 1 if idx == 0: fn = 'variation1_values' elif idx == 1: fn = 'variation2_values' variation_config = IVariationConfig(self.context) values = list(self.context.getField(fn).get(self.context)) var_dict = variation_config.getVariationDict() new_var_dict = {} pps = getMultiAdapter((self.context, self.request), name='plone_portal_state') language = pps.language() new_description = translate(_('label_new_description', default=u'New description'), domain='ftw.shop', context=self.context, target_language=language) DEFAULT_VARDATA = {'active':True, 'price': '0.00', 'skuCode': '99999', 'description': new_description} if len(variation_config.getVariationAttributes()) == 2: values1 = list(self.context.getField('variation1_values').get(self.context)) values2 = list(self.context.getField('variation2_values').get(self.context)) # Create a dict mapping old combination indexes to the new ones code_map = {} if idx == 0: for i in range(len(values1)): for j in range(len(values2)): old_vcode = "var-%s-%s" % (i, j) if i >= pos: new_vcode = "var-%s-%s" % (i + 1, j) code_map[old_vcode] = new_vcode else: code_map[old_vcode] = old_vcode elif idx == 1: for i in range(len(values1)): for j in range(len(values2)): old_vcode = "var-%s-%s" % (i, j) if j >= pos: new_vcode = "var-%s-%s" % (i, j + 1) code_map[old_vcode] = new_vcode else: code_map[old_vcode] = old_vcode # Based on the code map, reorder the var_dict for old_vcode in code_map.keys(): new_vcode = code_map[old_vcode] new_var_dict[new_vcode] = var_dict[old_vcode] # Now add some default variation data for the value just added if idx == 0: for j in range(len(values2)): vcode = "var-%s-%s" % (pos, j) new_var_dict[vcode] = DEFAULT_VARDATA elif idx == 1: for i in range(len(values1)): vcode = "var-%s-%s" % (i, pos) new_var_dict[vcode] = DEFAULT_VARDATA elif len(variation_config.getVariationAttributes()) == 1: assert(idx == 0) values1 = list(self.context.getField('variation1_values').get(self.context)) # Create a dict mapping old combination indexes to the new ones code_map = {} for i in range(len(values1)): old_vcode = "var-%s" % (i) if i >= pos: new_vcode = "var-%s" % (i + 1) code_map[old_vcode] = new_vcode else: code_map[old_vcode] = old_vcode # Based on the code map, reorder the var_dict for old_vcode in code_map.keys(): new_vcode = code_map[old_vcode] new_var_dict[new_vcode] = var_dict[old_vcode] # Now add some default variation data for the value just added vcode = "var-%s" % pos new_var_dict[vcode] = DEFAULT_VARDATA # Finally purge and update the var_dict variation_config.purge_dict() variation_config.updateVariationConfig(new_var_dict) pps = getMultiAdapter((self.context, self.request), name='plone_portal_state') language = pps.language() new_value = translate(_('label_new_value', default=u'New value'), domain='ftw.shop', context=self.context, target_language=language) values.insert(pos, new_value) self.context.getField(fn).set(self.context, values) if form.get('delvalue'): fn = None idx_and_pos = form.get('delvalue') idx, pos = idx_and_pos.split('-') idx = int(idx) pos = int(pos) if idx == 0: fn = 'variation1_values' elif idx == 1: fn = 'variation2_values' values = list(self.context.getField(fn).get(self.context)) variation_config = IVariationConfig(self.context) var_dict = variation_config.getVariationDict() new_var_dict = {} if len(variation_config.getVariationAttributes()) == 2: values1 = list(self.context.getField('variation1_values').get(self.context)) values2 = list(self.context.getField('variation2_values').get(self.context)) # Create a dict mapping old combination indexes to the new ones code_map = {} if idx == 0: for i in range(len(values1)): for j in range(len(values2)): old_vcode = "var-%s-%s" % (i, j) if i > pos: new_vcode = "var-%s-%s" % (i - 1, j) code_map[old_vcode] = new_vcode elif i == pos: code_map[old_vcode] = None else: code_map[old_vcode] = old_vcode elif idx == 1: for i in range(len(values1)): for j in range(len(values2)): old_vcode = "var-%s-%s" % (i, j) if j > pos: new_vcode = "var-%s-%s" % (i, j - 1) code_map[old_vcode] = new_vcode elif j == pos: code_map[old_vcode] = None else: code_map[old_vcode] = old_vcode # Based on the code map, reorder the var_dict for old_vcode in code_map.keys(): new_vcode = code_map[old_vcode] new_var_dict[new_vcode] = var_dict[old_vcode] elif len(variation_config.getVariationAttributes()) == 1: assert(idx == 0) values1 = list(self.context.getField('variation1_values').get(self.context)) # Create a dict mapping old combination indexes to the new ones code_map = {} for i in range(len(values1)): old_vcode = "var-%s" % (i) if i >= pos: new_vcode = "var-%s" % (i - 1) code_map[old_vcode] = new_vcode else: code_map[old_vcode] = old_vcode # Based on the code map, reorder the var_dict for old_vcode in code_map.keys(): new_vcode = code_map[old_vcode] new_var_dict[new_vcode] = var_dict[old_vcode] # # Finally purge and update the var_dict variation_config.purge_dict() variation_config.updateVariationConfig(new_var_dict) values.pop(int(pos)) self.context.getField(fn).set(self.context, values) if form.get('update_structure'): var_config = IVariationConfig(self.context) vattr0 = form.get('vattr-0', None) vattr1 = form.get('vattr-1', None) if len(var_config.getVariationAttributes()) >= 1: self.context.getField('variation1_attribute').set(self.context, vattr0) new_values = [] for i in range(len(var_config.getVariation1Values())): new_value = form.get('vvalue-%s-%s' % (0, i)) new_values.append(new_value) self.context.getField('variation1_values').set(self.context, new_values) if len(var_config.getVariationAttributes()) >= 2: self.context.getField('variation2_attribute').set(self.context, vattr1) new_values = [] for j in range(len(var_config.getVariation2Values())): new_value = form.get('vvalue-%s-%s' % (1, j)) new_values.append(new_value) self.context.getField('variation2_values').set(self.context, new_values) # Make sure we had a proper form submit, not just a GET request submitted = form.get('form.submitted', False) if submitted: variation_config = IVariationConfig(self.context) edited_var_data = self._parse_edit_variations_form() variation_config.updateVariationConfig(edited_var_data) IStatusMessage(self.request).addStatusMessage( _(u'msg_variations_saved', default=u"Variations saved."), type="info") self.request.RESPONSE.redirect(self.context.absolute_url()) return self.template()
def update_cart(self): """Update cart contents. """ context = aq_inner(self.context) ptool = getToolByName(context, 'plone_utils') # delete items with quantity 0 del_items = [] for item_key in self.get_items().keys(): try: qty = int(float(self.request.get('quantity_%s' % item_key))) if qty <= 0: del_items.append(item_key) except (ValueError, TypeError): ptool.addPortalMessage( _(u'msg_cart_invalidvalue', default=u"Invalid Values specified. Cart not updated."), 'error') referer = self.request.get('HTTP_REFERER', context.absolute_url()) self.request.response.redirect(referer) return for item_key in del_items: self._remove_item(item_key) # now update quantities and dimensions for item_key, item in self.get_items().items(): quantity = float(self.request.get('quantity_%s' % item_key)) dimensions = self.request.get('dimension_%s' % item_key, []) if not isinstance(dimensions, list): dimensions = [dimensions] if not validate_dimensions(dimensions, item['selectable_dimensions']): ptool.addPortalMessage( _(u'msg_cart_invalidvalue', default=u"Invalid Values specified. Cart not updated."), 'error') referer = self.request.get('HTTP_REFERER', context.absolute_url()) self.request.response.redirect(referer) return dimensions = map(Decimal, dimensions) # check that dimension changes do not collide item = self.get_items()[item_key] new_key = self.get_item_key( item['uid'], item['variation_code'] if 'variation_code' in item else '', dimensions) if new_key != item_key and new_key in self.get_items().keys(): ptool.addPortalMessage( _(u'msg_cart_invalidvalue', default=u"Invalid Values specified. Cart not updated."), 'error') referer = self.request.get('HTTP_REFERER', context.absolute_url()) self.request.response.redirect(referer) return self.update_item(item_key, quantity, dimensions) ptool.addPortalMessage(_(u'msg_cart_updated', default=u"Cart updated."), 'info') referer = self.request.get('HTTP_REFERER', context.absolute_url()) self.request.response.redirect(referer)
class DefaultPaymentProcessorStepGroup(BaseStepGroup): implements(IPaymentProcessorStepGroup) adapts(Interface, Interface, Interface) title = _(u"title_default_payment_processor_step_group", default="Default Payment Processor Choice") steps = (DefaultPaymentProcessorChoiceStep, )