Esempio n. 1
0
class AccountCacheGroup(orm.BaseModel):

    _kind = 135

    _use_rule_engine = False

    keys = orm.SuperStringProperty(
        repeated=True,
        indexed=False)  # stores 128bit md5 = can hold aprox 22k items

    def condition_taskqueue_or_admin(account, **kwargs):
        return account._is_taskqueue or account._root_admin

    _permissions = [
        orm.ExecuteActionPermission('update', condition_taskqueue_or_admin)
    ]

    _actions = [
        orm.Action(
            id='update',
            arguments={
                'ids': orm.SuperStringProperty(repeated=True),
                'keys':
                orm.SuperTextProperty(),  # compressed base64 encoded data
                'delete': orm.SuperBooleanProperty(default=False)
            },
            _plugin_groups=[
                orm.PluginGroup(plugins=[Context()]),
                orm.PluginGroup(transactional=True,
                                plugins=[AccountCacheGroupUpdate()])
            ]),
    ]
Esempio n. 2
0
class CountrySubdivision(orm.BaseModel):

  _kind = 13

  parent_record = orm.SuperKeyProperty('1', kind='13', indexed=False)
  code = orm.SuperStringProperty('2', required=True, indexed=False)
  name = orm.SuperStringProperty('3', required=True)
  complete_name = orm.SuperTextProperty('4', required=True)
  type = orm.SuperStringProperty('5', required=True, indexed=False)
  active = orm.SuperBooleanProperty('6', required=True, default=True)

  def condition_not_guest(account, **kwargs):
    return not account._is_guest

  def condition_true(**kwargs):
    return True

  _permissions = [
      orm.ExecuteActionPermission('search', condition_not_guest),
      orm.ReadFieldPermission(('parent_record', 'code', 'name', 'complete_name', 'type', 'active'), condition_true)
  ]

  _actions = [
      orm.Action(
          id='search',
          arguments={
              'search': orm.SuperSearchProperty(
                  default={'filters': [{'field': 'active', 'value': True, 'operator': '=='}],
                           'orders': [{'field': 'name', 'operator': 'asc'}]},
                  cfg={
                      'search_arguments': {'kind': '13', 'options': {'limit': 100}},
                      'ancestor_kind': '12',
                      'search_by_keys': True,
                      'filters': {'active': orm.SuperBooleanProperty(choices=(True,))},
                      'indexes': [{'ancestor': True,
                                   'filters': [('active', ['=='])],
                                   'orders': [('name', ['asc', 'desc'])]}]
                  }
              )
          },
          _plugin_groups=[
              orm.PluginGroup(
                  plugins=[
                      Context(),
                      GetCache(cfg={'group': 'search_13', 'cache': ['auth']}),
                      Read(),
                      RulePrepare(),
                      RuleExec(),
                      Search(),
                      RulePrepare(cfg={'path': '_entities'}),
                      Set(cfg={'d': {'output.entities': '_entities',
                                     'output.cursor': '_cursor',
                                     'output.more': '_more'}}),
                      CallbackExec()
                  ]
              )
          ]
      )
  ]
Esempio n. 3
0
class OrderCarrier(orm.BaseExpando):

    _kind = 123

    _use_rule_engine = False

    description = orm.SuperTextProperty('1', required=True)
    reference = orm.SuperVirtualKeyProperty('2',
                                            kind='113',
                                            required=True,
                                            indexed=False)
    unit_price = orm.SuperDecimalProperty('3', required=True, indexed=False)
    taxes = orm.SuperLocalStructuredProperty(OrderTax, '4', repeated=True)
    subtotal = orm.SuperDecimalProperty('5', required=True, indexed=False)
    tax_subtotal = orm.SuperDecimalProperty('6', required=True, indexed=False)
    total = orm.SuperDecimalProperty('7', required=True, indexed=False)

    _default_indexed = False
Esempio n. 4
0
class OrderProduct(orm.BaseExpando):

    _kind = 125

    _use_rule_engine = False

    reference = orm.SuperVirtualKeyProperty(
        '1', kind='28', required=True,
        indexed=False)  # the reference now has catalog->product key-path
    name = orm.SuperStringProperty('2', required=True, indexed=False)
    code = orm.SuperStringProperty('3', required=True, indexed=False)
    description = orm.SuperTextProperty('4', required=True)  # Soft limit 64kb.
    unit_price = orm.SuperDecimalProperty('5', required=True, indexed=False)
    quantity = orm.SuperDecimalProperty('6', required=True, indexed=False)

    _default_indexed = False

    _expando_fields = {
        'mass': orm.SuperDecimalProperty('7'),
        'volume': orm.SuperDecimalProperty('8')
    }
Esempio n. 5
0
class CatalogProduct(orm.BaseExpando):

    _kind = 28

    _use_rule_engine = False

    name = orm.SuperStringProperty('1', required=True, indexed=False)
    code = orm.SuperStringProperty('2', required=True, indexed=False)
    description = orm.SuperTextProperty('3', required=True)  # Soft limit 64kb.
    unit_price = orm.SuperDecimalProperty('4', required=True, indexed=False)
    availability = orm.SuperStringProperty(
        '5',
        required=True,
        indexed=False,
        choices=('in stock', 'available for order', 'out of stock',
                 'preorder'))
    image_width = orm.SuperIntegerProperty(
        '6', required=True, indexed=False
    )  # This could be removed if we stick to percentage possitioning, though that setup has not been tested!
    image_height = orm.SuperIntegerProperty(
        '7', required=True, indexed=False
    )  # This could be removed if we stick to percentage possitioning, though that setup has not been tested!
    position_top = orm.SuperFloatProperty(
        '8', required=True, indexed=False
    )  # This can represent percentage possition with three decimal precision (e.g. 99.999$)!
    position_left = orm.SuperFloatProperty(
        '9', required=True, indexed=False
    )  # This can represent percentage possition with three decimal precision (e.g. 99.999$)!

    _default_indexed = False

    _expando_fields = {
        'mass': orm.SuperDecimalProperty('10'),
        'volume': orm.SuperDecimalProperty('11')
    }

    def prepare(self, **kwargs):
        self.key = self.build_key(self.key_id_str,
                                  parent=kwargs.get('parent').parent())
Esempio n. 6
0
class OrderMessage(orm.BaseExpando):

    _kind = 35

    _use_rule_engine = False

    created = orm.SuperDateTimeProperty('1', required=True, auto_now_add=True)
    agent = orm.SuperKeyProperty('2', kind='11', required=True, indexed=False)
    action = orm.SuperKeyProperty('3', kind='1', required=True)
    body = orm.SuperTextProperty('4', required=True)

    _default_indexed = True

    _virtual_fields = {
        '_agent':
        orm.SuperReferenceStructuredProperty(
            '11',
            callback=lambda self: self.agent.get_async(),
            format_callback=lambda self, value: value),
        '_action':
        orm.SuperComputedProperty(lambda self: self.action.id()
                                  if self.action else '')
    }
Esempio n. 7
0
class Order(orm.BaseExpando):

    _kind = 34
    '''
  read:
    read_<order.account.id>
  search:
    search_34_<order.account.id>
  '''

    DELETE_CACHE_POLICY = {
        'group': [
            lambda context: 'read_34_%s' % context._order.key._root._id_str,
            'search_34_admin', 'search_34',
            lambda context: 'search_34_seller_%s' % context._order.
            seller_reference._root._id_str, lambda context:
            'search_34_buyer_%s' % context._order.key._root._id_str
        ]
    }

    created = orm.SuperDateTimeProperty('1', required=True, auto_now_add=True)
    updated = orm.SuperDateTimeProperty('2', required=True, auto_now=True)
    state = orm.SuperStringProperty(
        '3', required=True, default='cart',
        choices=('cart', 'order'))  # 'checkout', 'completed', 'canceled'
    date = orm.SuperDateTimeProperty('4', required=True)
    seller_reference = orm.SuperKeyProperty('5', kind='23', required=True)
    billing_address = orm.SuperLocalStructuredProperty('121', '6')
    shipping_address = orm.SuperLocalStructuredProperty('121', '7')
    currency = orm.SuperLocalStructuredProperty('17', '8', required=True)
    untaxed_amount = orm.SuperDecimalProperty('9',
                                              required=True,
                                              indexed=False)
    tax_amount = orm.SuperDecimalProperty('10', required=True, indexed=False)
    total_amount = orm.SuperDecimalProperty('11', required=True, indexed=False)
    payment_method = orm.SuperStringProperty(
        '12', required=False, choices=settings.AVAILABLE_PAYMENT_METHODS)
    payment_status = orm.SuperStringProperty('13',
                                             required=False,
                                             indexed=True)
    carrier = orm.SuperLocalStructuredProperty(OrderCarrier, '14')

    _default_indexed = False

    _virtual_fields = {
        '_seller':
        orm.SuperReferenceStructuredProperty('23',
                                             autoload=True,
                                             target_field='seller_reference'),
        '_tracker':
        orm.SuperReferenceProperty('136',
                                   autoload=True,
                                   callback=lambda self: self.get_tracker(),
                                   format_callback=lambda self, value: value),
        '_lines':
        orm.SuperRemoteStructuredProperty(OrderLine,
                                          repeated=True,
                                          search={
                                              'default': {
                                                  'filters': [],
                                                  'orders': [{
                                                      'field': 'sequence',
                                                      'operator': 'asc'
                                                  }]
                                              },
                                              'cfg': {
                                                  'indexes': [{
                                                      'ancestor':
                                                      True,
                                                      'filters': [],
                                                      'orders':
                                                      [('sequence', ['asc'])]
                                                  }],
                                              }
                                          }),
        '_messages':
        orm.SuperRemoteStructuredProperty(OrderMessage,
                                          repeated=True,
                                          search={
                                              'default': {
                                                  'filters': [],
                                                  'orders': [{
                                                      'field': 'created',
                                                      'operator': 'desc'
                                                  }]
                                              },
                                              'cfg': {
                                                  'indexes': [{
                                                      'ancestor':
                                                      True,
                                                      'filters': [],
                                                      'orders':
                                                      [('created', ['desc'])]
                                                  }],
                                              }
                                          }),
        '_seller_reference':
        orm.SuperComputedProperty(lambda self: self.seller_reference._structure
                                  if self.seller_reference else None),
    }

    def condition_taskqueue(account, **kwargs):
        return account._is_taskqueue

    def condition_cron(account, **kwargs):
        return account._is_cron

    def condition_not_guest_and_buyer_and_cart(account, entity, **kwargs):
        return not account._is_guest and entity._original.key_root == account.key \
            and entity._original.state == "cart"

    def condition_root_or_buyer_or_seller(account, entity, **kwargs):
        if entity._original.seller_reference is None:
            return False
        return account._root_admin or (not account._is_guest and (
            (entity._original.key_root == account.key) or
            (entity._original.seller_reference._root == account.key)))

    def condition_buyer_or_seller(account, entity, **kwargs):
        if entity._original.seller_reference is None:
            return False
        return not account._is_guest and (
            (entity._original.key_root == account.key) or
            (entity._original.seller_reference._root == account.key))

    def condition_search(account, action, entity, input, **kwargs):
        return action.key_id_str == "search" and (account._root_admin or (
            (not account._is_guest
             and input["search"]["filters"][0]["field"] == "seller_reference"
             and input["search"]["filters"][0]["value"]._root == account.key)
            or (not account._is_guest and "ancestor" in input["search"]
                and input["search"]["ancestor"]._root == account.key)))

    def condition_pay(action, entity, **kwargs):
        return action.key_id_str == "pay" and entity._original.state == "cart"

    def condition_notify(action, entity, **kwargs):
        return action.key_id_str == "notify" and entity._original.state == "order"

    def condition_update_line(account, entity, action, **kwargs):
        return not account._is_guest and entity._original.key_root == account.key \
            and entity._original.state == "cart" and action.key_id_str == "update_line"

    def condition_state(action, entity, **kwargs):
        return (action.key_id_str == "update_line" and entity.state == "cart") \
            or (action.key_id_str == "pay" and entity.state == "order")

    def condition_update_and_view_order(account, entity, action, **kwargs):
        return not account._is_guest and entity._original.key_root == account.key \
            and entity._original.state == "cart" and action.key_id_str in ("view_order", "update")

    def cache_group_search(context):
        key = 'search_34'
        _ancestor = context.input['search'].get('ancestor')
        filters = context.input['search'].get('filters')
        if context.account._root_admin:
            return '%s_admin' % key
        if filters and filters[0]['field'] == 'seller_reference' and filters[
                0]['value']._root == context.account.key:
            return '%s_seller_%s' % (key, context.account.key_id_str)
        if _ancestor and _ancestor._root == context.account.key:
            return '%s_buyer_%s' % (key, context.account.key_id_str)
        return key

    _permissions = [
        #  action.key_id_str not in ["search"] and...
        # Included payment_status in field permissions, will have to further analyse exclusion...
        orm.ExecuteActionPermission(
            ('update_line', 'view_order', 'update', 'delete', 'pay'),
            condition_not_guest_and_buyer_and_cart),
        orm.ExecuteActionPermission(('read'),
                                    condition_root_or_buyer_or_seller),
        orm.ExecuteActionPermission(('log_message'),
                                    condition_buyer_or_seller),
        orm.ExecuteActionPermission('search', condition_search),
        orm.ExecuteActionPermission('delete', condition_taskqueue),
        orm.ExecuteActionPermission(('cron', 'cron_notify'), condition_cron),
        orm.ExecuteActionPermission('see_messages', condition_buyer_or_seller),
        orm.ExecuteActionPermission('notify', condition_notify),
        orm.ReadFieldPermission(
            ('created', 'updated', 'state', 'date', 'seller_reference',
             'billing_address', 'shipping_address', 'currency',
             'untaxed_amount', 'tax_amount', 'total_amount', 'carrier',
             '_seller_reference', 'payment_status', 'payment_method', '_lines',
             '_messages.created', '_messages.agent', '_messages.action',
             '_messages.body', '_messages._action', '_tracker', '_seller.name',
             '_seller.logo', '_seller._stripe_publishable_key',
             '_seller._currency'), condition_root_or_buyer_or_seller),
        orm.WriteFieldPermission(
            ('date', 'seller_reference', 'currency', 'untaxed_amount',
             'tax_amount', 'total_amount', 'payment_method', '_lines',
             'carrier'), condition_update_line),
        orm.WriteFieldPermission('state', condition_state),
        orm.WriteFieldPermission(('payment_status', '_messages'),
                                 condition_pay),
        orm.WriteFieldPermission(('payment_status', '_messages'),
                                 condition_notify),
        orm.WriteFieldPermission('_messages', condition_buyer_or_seller),
        orm.WriteFieldPermission(
            ('date', 'shipping_address', 'billing_address', '_lines',
             'carrier', 'untaxed_amount', 'tax_amount', 'total_amount',
             'payment_method'), condition_update_and_view_order),
        orm.DenyWriteFieldPermission(
            ('_lines.taxes', '_lines.product.reference', '_lines.product.name',
             '_lines.product.code', '_lines.product.description',
             '_lines.product.unit_price', '_lines.product.mass',
             '_lines.product.volume'), condition_update_and_view_order)
    ]

    _actions = [
        orm.Action(id='update_line',
                   arguments={
                       'buyer': orm.SuperKeyProperty(kind='19', required=True),
                       'quantity': orm.SuperDecimalProperty(required=True),
                       'product': orm.SuperKeyProperty(kind='28',
                                                       required=True)
                   },
                   _plugin_groups=[
                       orm.PluginGroup(plugins=[
                           Context(),
                           OrderInit(),
                           OrderPluginExec(cfg={'kinds': ['117']}),
                           OrderProductSpecsFormat(),
                           OrderUpdateLine(),
                           OrderLineRemove(),
                           OrderStockManagement(),
                           OrderLineFormat(),
                           OrderCarrierFormat(),
                           OrderFormat(),
                           RulePrepare(),
                           RuleExec()
                       ]),
                       orm.PluginGroup(
                           transactional=True,
                           plugins=[
                               Write(),
                               DeleteCache(cfg=DELETE_CACHE_POLICY),
                               Set(cfg={'d': {
                                   'output.entity': '_order'
                               }})
                           ])
                   ]),
        orm.Action(
            id='view_order',
            arguments={
                'buyer': orm.SuperKeyProperty(kind='19', required=True),
                'seller': orm.SuperKeyProperty(kind='23', required=True),
                'read_arguments': orm.SuperJsonProperty()
            },
            _plugin_groups=[
                orm.PluginGroup(plugins=[
                    Context(),
                    GetCache(
                        cfg={
                            'group':
                            lambda context: 'read_34_%s' % context.input[
                                'buyer']._root._id_str,
                            'cache': ['account']
                        }),
                    OrderInit(),
                    OrderPluginExec(
                        cfg={'kinds': ['117']}
                    ),  # order currency must be available for everyone
                    OrderProductSpecsFormat(),
                    RulePrepare(),
                    RuleExec(),
                    Set(cfg={'d': {
                        'output.entity': '_order'
                    }}),
                    CallbackExec()
                ])
            ]),
        orm.Action(id='read',
                   arguments={
                       'key': orm.SuperKeyProperty(kind='34', required=True),
                       'read_arguments': orm.SuperJsonProperty()
                   },
                   _plugin_groups=[
                       orm.PluginGroup(plugins=[
                           Context(),
                           GetCache(
                               cfg={
                                   'group':
                                   lambda context: 'read_34_%s' % context.
                                   input['key']._root._id_str,
                                   'cache': ['account']
                               }),
                           Read(),
                           RulePrepare(),
                           RuleExec(),
                           Set(cfg={'d': {
                               'output.entity': '_order'
                           }}),
                           CallbackExec()
                       ])
                   ]),
        orm.Action(
            id='update',
            arguments={
                'key':
                orm.SuperKeyProperty(kind='34', required=True),
                'billing_address':
                orm.SuperLocalStructuredProperty('14'),
                'shipping_address':
                orm.SuperLocalStructuredProperty('14'),
                'carrier':
                orm.SuperVirtualKeyProperty(kind='113'),
                '_lines':
                orm.SuperLocalStructuredProperty(OrderLine, repeated=True),
                'read_arguments':
                orm.SuperJsonProperty()
            },
            _plugin_groups=[
                orm.PluginGroup(plugins=[
                    Context(),
                    Read(
                        cfg={
                            'read': {
                                '_lines': {
                                    'config': {
                                        'search': {
                                            'options': {
                                                'limit': 0
                                            }
                                        }
                                    }
                                }
                            }
                        }),
                    Set(cfg={'d': {
                        '_order._lines': 'input._lines'
                    }}),
                    OrderLineRemove(),
                    OrderStockManagement(),
                    OrderProductSpecsFormat(),
                    OrderFormat(
                    ),  # Needed for Carrier. Alternative is to break down this plugin in two, pre-carrier & post-carrier one.
                    OrderPluginExec(),
                    OrderLineFormat(),
                    OrderCarrierFormat(),
                    OrderFormat(),
                    RulePrepare(),
                    RuleExec()
                ]),
                orm.PluginGroup(transactional=True,
                                plugins=[
                                    Write(),
                                    RulePrepare(),
                                    DeleteCache(cfg=DELETE_CACHE_POLICY),
                                    Set(cfg={'d': {
                                        'output.entity': '_order'
                                    }})
                                ])
            ]),
        orm.Action(
            id='delete',
            arguments={'key': orm.SuperKeyProperty(kind='34', required=True)},
            _plugin_groups=[
                orm.PluginGroup(
                    plugins=[Context(
                    ), Read(), RulePrepare(),
                             RuleExec()]),
                orm.PluginGroup(transactional=True,
                                plugins=[
                                    Delete(),
                                    DeleteCache(cfg=DELETE_CACHE_POLICY),
                                    RulePrepare(),
                                    Set(cfg={'d': {
                                        'output.entity': '_order'
                                    }})
                                ])
            ]),
        orm.Action(
            id='search',
            arguments={
                'search':
                orm.SuperSearchProperty(
                    default={
                        'filters': [],
                        'orders': [{
                            'field': 'updated',
                            'operator': 'desc'
                        }]
                    },
                    cfg={
                        'search_arguments':
                        {
                            'kind': '34',
                            'options': {
                                'limit': settings.SEARCH_PAGE
                            }
                        },
                        'ancestor_kind':
                        '19',
                        'search_by_keys':
                        True,
                        'filters': {
                            'name':
                            orm.SuperStringProperty(),
                            'key':
                            orm.SuperVirtualKeyProperty(kind='34',
                                                        searchable=False),
                            'state':
                            orm.SuperStringProperty(repeated=True,
                                                    choices=('cart', 'order')),
                            'seller_reference':
                            orm.SuperKeyProperty(kind='23', searchable=False)
                        },
                        'indexes': [{
                            'orders': [('updated', ['asc', 'desc'])]
                        }, {
                            'orders': [('created', ['asc', 'desc'])]
                        }, {
                            'filters': [('key', ['=='])]
                        }, {
                            'filters': [('state', ['IN'])],
                            'orders': [('updated', ['asc', 'desc'])]
                        }, {
                            'ancestor': True,
                            'filters': [('state', ['IN'])],
                            'orders': [('updated', ['desc'])]
                        }, {
                            'filters': [('seller_reference', ['=='])],
                            'orders': [('updated', ['desc'])]
                        }]
                    })
            },
            _plugin_groups=[
                orm.PluginGroup(plugins=[
                    Context(),
                    GetCache(cfg={
                        'group': cache_group_search,
                        'cache': ['account']
                    }),
                    Read(),
                    RulePrepare(),
                    RuleExec(),
                    Search(),
                    RulePrepare(cfg={'path': '_entities'}),
                    Set(
                        cfg={
                            'd': {
                                'output.entities': '_entities',
                                'output.cursor': '_cursor',
                                'output.more': '_more'
                            }
                        }),
                    CallbackExec()
                ])
            ]),
        orm.Action(
            id='cron',
            arguments={},
            _plugin_groups=[
                orm.PluginGroup(plugins=[
                    Context(),
                    Read(),
                    RulePrepare(),
                    RuleExec(),
                    OrderCronDelete(
                        cfg={
                            'page': 100,
                            'cart_life': settings.ORDER_CART_LIFE,
                            'unpaid_order_life': settings.ORDER_UNPAID_LIFE
                        }),
                    CallbackExec()
                ])
            ]),
        orm.Action(
            id='notify',
            skip_csrf=True,
            arguments={
                'payment_method':
                orm.SuperStringProperty(
                    required=True, choices=settings.AVAILABLE_PAYMENT_METHODS),
                'request':
                orm.SuperPickleProperty(),
            },
            _plugin_groups=[
                orm.PluginGroup(plugins=[
                    Context(),
                    Read(),
                    OrderNotify(cfg={
                        'options': {
                            'paypal': {
                                'webscr': settings.PAYPAL_WEBSCR
                            }
                        }
                    }),
                    OrderSetMessage(
                        cfg={
                            'expando_fields': 'new_message_fields',
                            'expando_values': 'new_message'
                        }),
                    RulePrepare(),
                    RuleExec()
                ]),
                orm.PluginGroup(
                    transactional=True,
                    plugins=[
                        Write(),
                        RulePrepare(),
                        Set(cfg={'d': {
                            'output.entity': '_order'
                        }}),
                        # both seller and buyer must get the message
                        Notify(
                            cfg={
                                's': {
                                    'sender': settings.NOTIFY_EMAIL,
                                    'for_seller': False,
                                    'subject':
                                    notifications.ORDER_NOTIFY_SUBJECT,
                                    'body': notifications.ORDER_NOTIFY_BODY
                                },
                                'd': {
                                    'recipient': '_order.buyer_email'
                                }
                            }),
                        Notify(
                            cfg={
                                's': {
                                    'sender': settings.NOTIFY_EMAIL,
                                    'for_seller': True,
                                    'subject':
                                    notifications.ORDER_NOTIFY_SUBJECT,
                                    'body': notifications.ORDER_NOTIFY_BODY
                                },
                                'd': {
                                    'recipient': '_order.seller_email'
                                }
                            }),
                        DeleteCache(cfg=DELETE_CACHE_POLICY)
                    ])
            ]),
        orm.Action(
            id='pay',
            arguments={
                'key': orm.SuperKeyProperty(kind='34', required=True),
                'token': orm.SuperStringProperty(required=True),
                'read_arguments': orm.SuperJsonProperty()
            },
            _plugin_groups=[
                orm.PluginGroup(plugins=[
                    Context(),
                    Read(
                        cfg={
                            'read': {
                                '_lines': {
                                    'config': {
                                        'search': {
                                            'options': {
                                                'limit': 0
                                            }
                                        }
                                    }
                                }
                            }
                        })
                ]),
                orm.PluginGroup(
                    transactional=True,
                    plugins=[
                        # Transaction failures can still cause payment charges to succeed.
                        # Isolate as little plugins as possible in transaction to minimize transaction failures.
                        # We can also implement a two step payment to make the payment more robust.
                        OrderPay(),
                        OrderSetMessage(
                            cfg={
                                'expando_fields': 'new_message_fields',
                                'expando_values': 'new_message'
                            }),
                        RulePrepare(),
                        RuleExec(),
                        Write(),
                        RulePrepare(),
                        DeleteCache(cfg=DELETE_CACHE_POLICY),
                        Set(cfg={'d': {
                            'output.entity': '_order'
                        }})
                    ]),
                orm.PluginGroup(plugins=[
                    # both seller and buyer must get the message
                    Notify(
                        cfg={
                            's': {
                                'sender': settings.NOTIFY_EMAIL,
                                'for_seller': False,
                                'subject': notifications.ORDER_NOTIFY_SUBJECT,
                                'body': notifications.ORDER_NOTIFY_BODY
                            },
                            'd': {
                                'recipient': '_order.buyer_email'
                            }
                        }),
                    Notify(
                        cfg={
                            's': {
                                'sender': settings.NOTIFY_EMAIL,
                                'for_seller': True,
                                'subject': notifications.ORDER_NOTIFY_SUBJECT,
                                'body': notifications.ORDER_NOTIFY_BODY
                            },
                            'd': {
                                'recipient': '_order.seller_email'
                            }
                        })
                ])
            ]),
        orm.Action(
            id='see_messages',
            arguments={'key': orm.SuperKeyProperty(kind='34', required=True)},
            _plugin_groups=[
                orm.PluginGroup(
                    plugins=[Context(
                    ), Read(), RulePrepare(),
                             RuleExec()]),
                orm.PluginGroup(transactional=True,
                                plugins=[
                                    OrderNotifyTrackerSeen(),
                                    DeleteCache(cfg=DELETE_CACHE_POLICY)
                                ])
            ]),
        orm.Action(
            id='log_message',
            arguments={
                'key':
                orm.SuperKeyProperty(kind='34', required=True),
                'message':
                orm.SuperTextProperty(required=True,
                                      max_size=settings.MAX_MESSAGE_SIZE)
            },
            _plugin_groups=[
                orm.PluginGroup(plugins=[
                    Context(),
                    Read(),
                    OrderSetMessage(),
                    RulePrepare(),
                    RuleExec()
                ]),
                orm.PluginGroup(transactional=True,
                                plugins=[
                                    Write(),
                                    Set(cfg={'d': {
                                        'output.entity': '_order'
                                    }}),
                                    OrderNotifyTrackerSet(
                                        cfg=settings.ORDER_CRON_NOTIFY_TIMER),
                                    DeleteCache(cfg=DELETE_CACHE_POLICY)
                                ])
            ]),
        orm.Action(
            id='cron_notify',
            arguments={},
            _plugin_groups=[
                orm.PluginGroup(
                    plugins=[Context(
                    ), Read(), RulePrepare(),
                             RuleExec()]),
                orm.PluginGroup(
                    transactional=False,
                    plugins=[
                        # This plugin isolates its parts in transaction, so the group is wrapped in transaction.
                        OrderCronNotify(
                            cfg={
                                's': {
                                    'sender': settings.NOTIFY_EMAIL,
                                    'subject':
                                    notifications.ORDER_LOG_MESSAGE_SUBJECT,
                                    'body':
                                    notifications.ORDER_LOG_MESSAGE_BODY
                                },
                                'hours':
                                settings.ORDER_CRON_NOTIFY_TIMER['hours'],
                                'minutes':
                                settings.ORDER_CRON_NOTIFY_TIMER['minutes'],
                                'seconds':
                                settings.ORDER_CRON_NOTIFY_TIMER['seconds']
                            }),
                        CallbackExec()
                    ])
            ])
    ]

    @property
    def buyer_email(self):
        account = self.root_entity
        account.read()
        return account._primary_email

    @property
    def seller_email(self):
        account = self.seller_reference._root.entity
        account.read()
        return account._primary_email

    @property
    def seller_and_buyer_emails(self):
        emails = []
        emails.append(self.seller_email)
        emails.append(self.buyer_email)
        return emails

    def get_tracker(self):
        tracker = None
        if self.key:
            tracker = OrderNotifyTracker.build_key(self.key.urlsafe()).get()
        return tracker
Esempio n. 8
0
class Catalog(orm.BaseExpando):

    _kind = 31

    DELETE_CACHE_POLICY = {
        # only delete public cache when user saves published or indexed catalog
        'satisfy': [(['search_31'], lambda context, group_id: True if
                     (context._catalog.state == 'indexed' or
                      (context._catalog.state != 'indexed' and
                       (hasattr(context, 'catalog_original_state') and context.
                        catalog_original_state == 'indexed'))) else False)],
        'group': [
            'search_31', 'search_31_admin',
            lambda context: 'read_31_%s' % context._catalog.key._id_str,
            lambda context: 'search_31_%s' % context._catalog.key._root._id_str
        ]
    }

    created = orm.SuperDateTimeProperty('1', required=True, auto_now_add=True)
    updated = orm.SuperDateTimeProperty('2', required=True, auto_now=True)
    name = orm.SuperStringProperty('3', required=True)
    published_date = orm.SuperDateTimeProperty('4', required=False)
    discontinued_date = orm.SuperDateTimeProperty('5', required=False)
    state = orm.SuperStringProperty('6',
                                    required=True,
                                    default='draft',
                                    choices=('draft', 'published', 'indexed',
                                             'discontinued'))

    _default_indexed = False

    _expando_fields = {
        'cover':
        orm.SuperImageLocalStructuredProperty(CatalogImage,
                                              '7',
                                              process_config={
                                                  'copy': True,
                                                  'copy_name': 'cover',
                                                  'transform': True,
                                                  'width': 240,
                                                  'height': 312,
                                                  'crop_to_fit': True
                                              })
    }

    _virtual_fields = {
        '_images':
        orm.SuperImageRemoteStructuredProperty(CatalogImage,
                                               repeated=True,
                                               search={
                                                   'default': {
                                                       'filters': [],
                                                       'orders': [{
                                                           'field':
                                                           'sequence',
                                                           'operator':
                                                           'desc'
                                                       }]
                                                   },
                                                   'cfg': {
                                                       'indexes': [{
                                                           'ancestor':
                                                           True,
                                                           'filters': [],
                                                           'orders':
                                                           [('sequence',
                                                             ['desc'])]
                                                       }],
                                                   }
                                               }),
        '_seller':
        orm.SuperReferenceStructuredProperty(
            '23',
            autoload=True,
            callback=lambda self: self.key.parent().get_async())
    }

    def condition_not_guest(account, **kwargs):
        return not account._is_guest

    def condition_not_guest_and_owner_or_root(account, entity, **kwargs):
        return not account._is_guest and (
            entity._original.key_root == account.key or account._root_admin)

    def condition_search(account, entity, action, input, **kwargs):
        def valid_search():
            if action.key_id == 'search':
                _ancestor = input['search'].get('ancestor')
                _filters = input['search'].get('filters')
                if _filters:
                    field = _filters[0]['field']
                    op = _filters[0]['operator']
                    value = _filters[0]['value']
                    if field == 'state' and op == 'IN':
                        if value == ['indexed']:  # home page
                            return True
                        else:
                            if _ancestor:
                                if 'discontinued' not in value:  # seller catalogs view
                                    if not account._is_guest and _ancestor._root == account.key:
                                        return True
                                if value == ['published',
                                             'indexed']:  # seller profile view
                                    return True
            return False

        return account._root_admin or valid_search()

    def condition_published_or_indexed(entity, **kwargs):
        return entity._original.state in ("published", "indexed")

    def condition_update(account, entity, **kwargs):
        return not account._is_guest and entity._original.key_root == account.key \
            and (entity._original.state in ("draft", "published", "indexed"))

    def condition_not_guest_and_owner_and_draft(account, entity, **kwargs):
        return not account._is_guest and entity._original.key_root == account.key \
            and entity._original.state == "draft"

    def condition_deny_write_field_permission(account, entity, action,
                                              **kwargs):
        return not account._is_guest and entity._original.key_root == account.key \
            and entity._original.state == "draft" and action.key_id_str == "update"

    def condition_not_guest_and_owner_and_published(account, entity, **kwargs):
        return not account._is_guest and entity._original.key_root == account.key \
            and entity._original.state in ("published", "indexed")

    def condition_root(account, **kwargs):
        return account._root_admin

    def condition_taskqueue(account, **kwargs):
        return account._is_taskqueue

    def condition_cron(account, **kwargs):
        return account._is_cron

    def condition_true(**kwargs):
        return True

    def condition_false(**kwargs):
        return False

    def condition_write_images(account, entity, action, **kwargs):
        return not account._is_guest and entity._original.key_root == account.key \
            and entity._original.state == "draft" \
            and action.key_id_str \
            in ("read", "catalog_upload_images", "prepare")

    def condition_write_state(entity, action, **kwargs):
        return (action.key_id_str == "create" and entity.state == "draft") \
            or (action.key_id_str == "publish" and entity.state == "published") \
            or (action.key_id_str == "sudo_discontinue" and entity.state == "discontinued") \
            or (action.key_id_str == "discontinue" and entity.state == "discontinued") \
            or (action.key_id_str == "sudo" and entity.state != "draft")

    def condition_write_discontinued_date(entity, action, **kwargs):
        return action.key_id_str in ("sudo_discontinue", "discontinue",
                                     "sudo") and entity.state == "discontinued"

    def condition_write_published_date(entity, action, **kwargs):
        return action.key_id_str == "sudo" and entity.state in ("published",
                                                                "indexed")

    def condition_duplicate(action, **kwargs):
        return action.key_id_str in ("catalog_process_duplicate")

    def cache_read(context):
        if context.input[
                'key']._root == context.account.key or context.account._root_admin:
            return 'account'
        else:
            return None

    def cache_search(context):
        _ancestor = context.input['search'].get('ancestor')
        if context.account._root_admin or (_ancestor and _ancestor._root
                                           == context.account.key):
            return 'account'
        return None

    def cache_group_search(context):
        key = 'search_31'
        _ancestor = context.input['search'].get('ancestor')
        if context.account._root_admin:
            return '%s_admin' % key
        if _ancestor and _ancestor._root == context.account.key:
            return '%s_%s' % (key, context.account.key_id_str)
        return key

    _permissions = [
        orm.ExecuteActionPermission('prepare', condition_not_guest),
        orm.ExecuteActionPermission('create',
                                    condition_not_guest_and_owner_or_root),
        orm.ExecuteActionPermission('search', condition_search),
        orm.ExecuteActionPermission('read', condition_published_or_indexed),
        orm.ExecuteActionPermission('update', condition_update),
        orm.ExecuteActionPermission(
            ('read', 'publish', 'catalog_upload_images'),
            condition_not_guest_and_owner_and_draft),
        orm.ExecuteActionPermission(
            ('discontinue', 'catalog_duplicate'),
            condition_not_guest_and_owner_and_published),
        orm.ExecuteActionPermission(('read', 'sudo'), condition_root),
        orm.ExecuteActionPermission('cron', condition_cron),
        orm.ExecuteActionPermission(('account_discontinue', 'sudo_discontinue',
                                     'catalog_process_duplicate', 'delete'),
                                    condition_taskqueue),
        # field permissions
        orm.ReadFieldPermission(
            ('created', 'updated', 'name', 'published_date',
             'discontinued_date', 'state', 'cover', '_images'),
            condition_not_guest_and_owner_or_root),
        orm.WriteFieldPermission(('name', 'published_date',
                                  'discontinued_date', 'cover', '_images'),
                                 condition_not_guest_and_owner_and_draft),
        orm.DenyWriteFieldPermission(
            ('_images.image', '_images.content_type', '_images.size',
             '_images.gs_object_name', '_images.serving_url'),
            condition_deny_write_field_permission),
        orm.WriteFieldPermission(('_images'), condition_write_images),
        orm.WriteFieldPermission(('_images.products.availability', ),
                                 condition_not_guest_and_owner_and_published),
        orm.WriteFieldPermission('state', condition_write_state),
        orm.WriteFieldPermission('discontinued_date',
                                 condition_write_discontinued_date),
        orm.WriteFieldPermission('published_date',
                                 condition_write_published_date),
        orm.ReadFieldPermission(
            ('created', 'updated', 'name', 'published_date',
             'discontinued_date', 'state', 'cover', '_images'),
            condition_published_or_indexed),
        orm.ReadFieldPermission(
            ('_seller.name', '_seller.logo', '_seller._currency'),
            condition_true),
        orm.WriteFieldPermission(
            ('created', 'updated', 'name', 'published_date',
             'discontinued_date', 'state', 'cover', '_images'),
            condition_duplicate)
    ]

    _actions = [
        orm.Action(id='prepare',
                   arguments={
                       'seller': orm.SuperKeyProperty(kind='23', required=True)
                   },
                   _plugin_groups=[
                       orm.PluginGroup(plugins=[
                           Context(),
                           Read(),
                           RulePrepare(),
                           RuleExec(),
                           Set(cfg={'d': {
                               'output.entity': '_catalog'
                           }})
                       ])
                   ]),
        orm.Action(id='create',
                   arguments={
                       'seller': orm.SuperKeyProperty(kind='23',
                                                      required=True),
                       'name': orm.SuperStringProperty(required=True)
                   },
                   _plugin_groups=[
                       orm.PluginGroup(plugins=[
                           Context(),
                           Read(),
                           Set(
                               cfg={
                                   's': {
                                       '_catalog.state': 'draft'
                                   },
                                   'd': {
                                       '_catalog.name': 'input.name'
                                   }
                               }),
                           RulePrepare(),
                           RuleExec()
                       ]),
                       orm.PluginGroup(
                           transactional=True,
                           plugins=[
                               Write(),
                               DeleteCache(cfg=DELETE_CACHE_POLICY),
                               Set(cfg={'d': {
                                   'output.entity': '_catalog'
                               }})
                           ])
                   ]),
        orm.Action(id='read',
                   arguments={
                       'key': orm.SuperKeyProperty(kind='31', required=True),
                       'read_arguments': orm.SuperJsonProperty()
                   },
                   _plugin_groups=[
                       orm.PluginGroup(plugins=[
                           Context(),
                           GetCache(
                               cfg={
                                   'group':
                                   lambda context: 'read_31_%s' % context.
                                   input['key']._id_str,
                                   'cache': [cache_read, 'all']
                               }),
                           Read(),
                           RulePrepare(),
                           RuleExec(),
                           Set(cfg={'d': {
                               'output.entity': '_catalog'
                           }}),
                           CallbackExec()
                       ])
                   ]),
        orm.Action(id='update',
                   arguments={
                       'key':
                       orm.SuperKeyProperty(kind='31', required=True),
                       'name':
                       orm.SuperStringProperty(required=True),
                       '_images':
                       orm.SuperImageRemoteStructuredProperty(CatalogImage,
                                                              repeated=True),
                       'read_arguments':
                       orm.SuperJsonProperty()
                   },
                   _plugin_groups=[
                       orm.PluginGroup(plugins=[
                           Context(),
                           Read(),
                           Set(
                               cfg={
                                   'd': {
                                       '_catalog.name': 'input.name',
                                       'catalog_original_state':
                                       '_catalog._original.state',
                                       '_catalog._images': 'input._images'
                                   }
                               }),
                           CatalogProcessCoverSet(),
                           CatalogProcessProducts(),
                           RulePrepare(),
                           RuleExec()
                       ]),
                       orm.PluginGroup(
                           transactional=True,
                           plugins=[
                               Write(),
                               DeleteCache(cfg=DELETE_CACHE_POLICY),
                               Set(cfg={'d': {
                                   'output.entity': '_catalog'
                               }})
                           ])
                   ]),
        orm.Action(id='catalog_upload_images',
                   arguments={
                       'key':
                       orm.SuperKeyProperty(kind='31', required=True),
                       '_images':
                       orm.SuperImageLocalStructuredProperty(CatalogImage,
                                                             upload=True,
                                                             repeated=True),
                       'read_arguments':
                       orm.SuperJsonProperty()
                   },
                   _plugin_groups=[
                       orm.PluginGroup(plugins=[
                           Context(),
                           Read(),
                           UploadImages(
                               cfg={
                                   'path': '_catalog._images',
                                   'images_path': 'input._images'
                               }),
                           CatalogProcessCoverSet(),
                           RulePrepare(),
                           RuleExec()
                       ]),
                       orm.PluginGroup(
                           transactional=True,
                           plugins=[
                               Write(),
                               DeleteCache(cfg=DELETE_CACHE_POLICY),
                               Set(cfg={'d': {
                                   'output.entity': '_catalog'
                               }})
                           ])
                   ]),
        orm.Action(
            id='delete',
            arguments={'key': orm.SuperKeyProperty(kind='31', required=True)},
            _plugin_groups=[
                orm.PluginGroup(
                    plugins=[Context(
                    ), Read(), RulePrepare(),
                             RuleExec()]),
                orm.PluginGroup(
                    transactional=True,
                    plugins=[
                        Delete(),
                        DeleteCache(cfg=DELETE_CACHE_POLICY),
                        Set(cfg={'d': {
                            'output.entity': '_catalog'
                        }})
                    ])
            ]),
        orm.Action(
            id='search',
            arguments={
                'search':
                orm.SuperSearchProperty(
                    default={
                        'filters': [],
                        'orders': [{
                            'field': 'created',
                            'operator': 'desc'
                        }]
                    },
                    cfg={
                        'search_arguments':
                        {
                            'kind': '31',
                            'options': {
                                'limit': settings.SEARCH_PAGE
                            }
                        },
                        'ancestor_kind':
                        '23',
                        'search_by_keys':
                        True,
                        'filters': {
                            'name':
                            orm.SuperStringProperty(),
                            'key':
                            orm.SuperVirtualKeyProperty(kind='31',
                                                        searchable=False),
                            'state':
                            orm.SuperStringProperty(
                                repeated=True,
                                choices=('published', 'indexed',
                                         'discontinued', 'draft'))
                        },
                        'indexes': [{
                            'ancestor': True,
                            'orders': [('created', ['desc'])]
                        }, {
                            'ancestor':
                            True,
                            'filters': [('state', ['IN'])],
                            'orders': [('created', ['desc']),
                                       ('key', ['desc'])]
                        }, {
                            'ancestor':
                            True,
                            'filters': [('state', ['IN'])],
                            'orders': [('published_date', ['desc']),
                                       ('key', ['desc'])]
                        }, {
                            'orders': [('created', ['asc', 'desc'])]
                        }, {
                            'orders': [('updated', ['asc', 'desc'])]
                        }, {
                            'orders': [('published_date', ['asc', 'desc'])]
                        }, {
                            'orders': [('discontinued_date', ['asc', 'desc'])]
                        }, {
                            'filters': [('state', ['IN'])],
                            'orders': [('published_date', ['desc'])]
                        }, {
                            'filters': [('key', ['=='])]
                        }]
                    })
            },
            _plugin_groups=[
                orm.PluginGroup(plugins=[
                    Context(),
                    GetCache(
                        cfg={
                            'group': cache_group_search,
                            'cache': ['admin', cache_search, 'all']
                        }),
                    Read(),
                    RulePrepare(cfg={'d': {
                        'input': 'input'
                    }}),
                    RuleExec(),
                    Search(),
                    RulePrepare(cfg={'path': '_entities'}),
                    Set(
                        cfg={
                            'd': {
                                'output.entities': '_entities',
                                'output.cursor': '_cursor',
                                'output.more': '_more'
                            }
                        }),
                    CallbackExec()
                ])
            ]),
        orm.Action(
            id='publish',
            arguments={'key': orm.SuperKeyProperty(kind='31', required=True)},
            _plugin_groups=[
                orm.PluginGroup(plugins=[
                    Context(),
                    Read(),
                    Set(
                        cfg={
                            's': {
                                '_catalog.state': 'published'
                            },
                            'd': {
                                'catalog_original_state':
                                '_catalog._original.state'
                            },
                            'f': {
                                '_catalog.published_date':
                                lambda: datetime.datetime.now()
                            }
                        }),
                    RulePrepare(),
                    RuleExec()
                ]),
                orm.PluginGroup(
                    transactional=True,
                    plugins=[
                        Write(),
                        RulePrepare(),
                        Set(cfg={'d': {
                            'output.entity': '_catalog'
                        }}),
                        # notify when user publishes catalog
                        Notify(
                            cfg={
                                's': {
                                    'subject':
                                    notifications.CATALOG_PUBLISH_SUBJECT,
                                    'body': notifications.CATALOG_PUBLISH_BODY,
                                    'sender': settings.NOTIFY_EMAIL
                                },
                                'd': {
                                    'recipient':
                                    '_catalog.root_entity._primary_email'
                                }
                            }),
                        DeleteCache(cfg=DELETE_CACHE_POLICY)
                    ])
            ]),
        orm.Action(
            id='sudo_discontinue',
            arguments={'key': orm.SuperKeyProperty(kind='31', required=True)},
            _plugin_groups=[
                orm.PluginGroup(plugins=[
                    Context(),
                    Read(),
                    Set(
                        cfg={
                            's': {
                                '_catalog.state': 'discontinued'
                            },
                            'd': {
                                'catalog_original_state':
                                '_catalog._original.state'
                            },
                            'f': {
                                '_catalog.discontinued_date':
                                lambda: datetime.datetime.now()
                            }
                        }),
                    RulePrepare(),
                    RuleExec()
                ]),
                orm.PluginGroup(
                    transactional=True,
                    plugins=[
                        Write(),
                        RulePrepare(),
                        Set(cfg={'d': {
                            'output.entity': '_catalog'
                        }}),
                        # notify owner when catalog gets discontinued
                        Notify(
                            cfg={
                                's': {
                                    'subject':
                                    notifications.CATALOG_SUDO_SUBJECT,
                                    'body': notifications.
                                    CATALOG_SUDO_DISCONTINUE_BODY,
                                    'sender': settings.NOTIFY_EMAIL
                                },
                                'd': {
                                    'recipient':
                                    '_catalog.root_entity._primary_email'
                                }
                            }),
                        DeleteCache(cfg=DELETE_CACHE_POLICY)
                    ])
            ]),
        orm.Action(
            id='discontinue',
            arguments={'key': orm.SuperKeyProperty(kind='31', required=True)},
            _plugin_groups=[
                orm.PluginGroup(plugins=[
                    Context(),
                    Read(),
                    Set(
                        cfg={
                            's': {
                                '_catalog.state': 'discontinued'
                            },
                            'd': {
                                'catalog_original_state':
                                '_catalog._original.state'
                            },
                            'f': {
                                '_catalog.discontinued_date':
                                lambda: datetime.datetime.now()
                            }
                        }),
                    RulePrepare(),
                    RuleExec()
                ]),
                orm.PluginGroup(
                    transactional=True,
                    plugins=[
                        Write(),
                        RulePrepare(),
                        Set(cfg={'d': {
                            'output.entity': '_catalog'
                        }}),
                        # notify owner when catalog gets discontinued
                        Notify(
                            cfg={
                                's': {
                                    'subject':
                                    notifications.CATALOG_DISCONTINUE_SUBJECT,
                                    'body':
                                    notifications.CATALOG_DISCONTINUE_BODY,
                                    'sender': settings.NOTIFY_EMAIL
                                },
                                'd': {
                                    'recipient':
                                    '_catalog.root_entity._primary_email'
                                }
                            }),
                        DeleteCache(cfg=DELETE_CACHE_POLICY)
                    ])
            ]),
        orm.Action(
            id='account_discontinue',
            arguments={
                'account': orm.SuperKeyProperty(kind='11', required=True)
            },
            _plugin_groups=[
                orm.PluginGroup(
                    plugins=[Context(
                    ), Read(), RulePrepare(),
                             RuleExec()]),
                orm.PluginGroup(transactional=True,
                                plugins=[CatalogDiscontinue(),
                                         CallbackExec()])
            ]),
        orm.Action(
            id='sudo',
            arguments={
                'key':
                orm.SuperKeyProperty(kind='31', required=True),
                'state':
                orm.SuperStringProperty(required=True,
                                        choices=('published', 'indexed',
                                                 'discontinued')),
                'message':
                orm.SuperTextProperty(required=True),
                'note':
                orm.SuperTextProperty(required=True)
            },
            _plugin_groups=[
                orm.PluginGroup(plugins=[
                    Context(),
                    Read(),
                    Set(
                        cfg={
                            'd': {
                                '_catalog.state':
                                'input.state',
                                'catalog_original_state':
                                '_catalog._original.state'
                            },
                            'f': {
                                '_catalog.published_date':
                                lambda: datetime.datetime.now(),
                                '_catalog.discontinued_date':
                                lambda: datetime.datetime.now()
                            }
                        }
                    ),  # ATM permissions handle if this field is writable.
                    RulePrepare(),
                    RuleExec()
                ]),
                orm.PluginGroup(
                    transactional=True,
                    plugins=[
                        Write(),
                        RulePrepare(),
                        Set(cfg={'d': {
                            'output.entity': '_catalog'
                        }}),
                        # use 1 notify plugin with dynamic email
                        Notify(
                            cfg={
                                's': {
                                    'subject':
                                    notifications.CATALOG_SUDO_SUBJECT,
                                    'body': notifications.CATALOG_SUDO_BODY,
                                    'sender': settings.NOTIFY_EMAIL
                                },
                                'd': {
                                    'recipient':
                                    '_catalog.root_entity._primary_email'
                                }
                            }),
                        DeleteCache(cfg=DELETE_CACHE_POLICY)
                    ])
            ]),
        orm.Action(
            id='cron',
            arguments={},
            _plugin_groups=[
                orm.PluginGroup(plugins=[
                    Context(),
                    Read(),
                    RulePrepare(),
                    RuleExec(),
                    CatalogCronDelete(
                        cfg={
                            'page': 100,
                            'unpublished_life':
                            settings.CATALOG_UNPUBLISHED_LIFE,
                            'discontinued_life':
                            settings.CATALOG_DISCONTINUED_LIFE
                        }),
                    CallbackExec()
                ])
            ]),
        orm.Action(id='catalog_duplicate',
                   arguments={
                       'key': orm.SuperKeyProperty(kind='31', required=True),
                       'channel': orm.SuperStringProperty(required=True)
                   },
                   _plugin_groups=[
                       orm.PluginGroup(plugins=[
                           Context(),
                           Read(),
                           RulePrepare(),
                           RuleExec(),
                           Set(cfg={'d': {
                               'output.entity': '_catalog'
                           }}),
                           CallbackExec(cfg=[('callback', {
                               'action_id': 'catalog_process_duplicate',
                               'action_model': '31'
                           }, {
                               'key': '_catalog.key_urlsafe',
                               'channel': 'input.channel'
                           }, None)])
                       ])
                   ]),
        orm.Action(
            id='catalog_process_duplicate',
            arguments={
                'key': orm.SuperKeyProperty(kind='31', required=True),
                'channel': orm.SuperStringProperty(required=True)
            },
            _plugin_groups=[
                orm.PluginGroup(
                    plugins=[Context(
                    ), Read(), RulePrepare(),
                             RuleExec()]),
                orm.PluginGroup(
                    transactional=True,
                    plugins=[
                        Duplicate(),
                        Set(
                            cfg={
                                's': {
                                    '_catalog.state': 'draft'
                                },
                                'rm': ['_catalog.created']
                            }),
                        Write(),
                        # notify duplication process complete via channel
                        Notify(
                            cfg={
                                's': {
                                    'sender': settings.NOTIFY_EMAIL
                                },
                                'd': {
                                    'recipient': 'input.channel',
                                    'catalog_key': '_catalog.key_urlsafe'
                                },
                                'method': 'channel'
                            }),
                        DeleteCache(cfg=DELETE_CACHE_POLICY)
                    ])
            ])
    ]

    @classmethod
    def prepare_key(cls, input, **kwargs):
        return cls.build_key(None, parent=input.get('seller'))
Esempio n. 9
0
class Account(orm.BaseExpando):

    _kind = 11

    _use_record_engine = True
    '''
  Cache:
  11_<account.id>
  '''

    READ_CACHE_POLICY = {
        'group': lambda context: '11_%s' % context.account.key_id_str,
        'cache': ['account']
    }
    DELETE_CACHE_POLICY = {
        'group': [
            'admin', lambda context: '11_%s' % context._account.key_id_str,
            lambda context: '11_%s' % context.account.key_id_str
        ]
    }

    created = orm.SuperDateTimeProperty('1', required=True, auto_now_add=True)
    updated = orm.SuperDateTimeProperty('2', required=True, auto_now=True)
    state = orm.SuperStringProperty('3',
                                    required=True,
                                    default='active',
                                    choices=('active', 'suspended'))
    identities = orm.SuperStructuredProperty(
        AccountIdentity, '4', repeated=True)  # Soft limit 100 instances.
    sessions = orm.SuperLocalStructuredProperty(
        AccountSession, '5', repeated=True)  # Soft limit 100 instances.

    _default_indexed = False

    _virtual_fields = {
        'ip_address':
        orm.SuperComputedProperty(lambda self: tools.get_remote_addr()),
        '_primary_email':
        orm.SuperComputedProperty(lambda self: self.primary_email()),
        '_csrf':
        orm.SuperComputedProperty(lambda self: self.get_csrf()),
        '_records':
        orm.SuperRecordProperty('11')
    }

    def condition_guest_and_active(entity, **kwargs):
        return entity._is_guest or entity._original.state == "active"

    def condition_true(entity, **kwargs):
        return True

    def condition_not_guest_and_owner(account, entity, **kwargs):
        return not account._is_guest and account.key == entity._original.key

    def condition_not_guest(account, **kwargs):
        return not account._is_guest

    def condition_root(account, **kwargs):
        return account._root_admin

    def condition_sudo_action_and_root(account, action, **kwargs):
        return action.key_id_str == "sudo" and account._root_admin

    _permissions = [
        orm.ExecuteActionPermission('login', condition_guest_and_active),
        orm.ExecuteActionPermission('current_account', condition_true),
        orm.ExecuteActionPermission(('read', 'update', 'logout'),
                                    condition_not_guest_and_owner),
        orm.ExecuteActionPermission(('blob_upload_url', 'create_channel'),
                                    condition_not_guest),
        orm.ExecuteActionPermission(('read', 'search', 'sudo'),
                                    condition_root),
        orm.ReadFieldPermission(('created', 'updated', 'state', 'identities',
                                 'sessions', '_primary_email'),
                                condition_not_guest_and_owner),
        orm.ReadFieldPermission(
            ('created', 'updated', 'state', 'identities', 'sessions',
             '_primary_email', 'ip_address', '_records'), condition_root),
        orm.WriteFieldPermission(
            ('state', 'identities', 'sessions', '_primary_email', '_records'),
            condition_not_guest_and_owner),
        orm.WriteFieldPermission(('state', 'sessions', '_records'),
                                 condition_sudo_action_and_root)
    ]

    _actions = [
        orm.Action(id='login',
                   skip_csrf=True,
                   arguments={
                       'login_method':
                       orm.SuperStringProperty(
                           required=True,
                           choices=[
                               login_method['type']
                               for login_method in settings.LOGIN_METHODS
                           ]),
                       'code':
                       orm.SuperStringProperty(),
                       'error_message':
                       orm.SuperStringProperty(),
                       'state':
                       orm.SuperStringProperty(),
                       'error':
                       orm.SuperStringProperty(),
                       'redirect_to':
                       orm.SuperStringProperty()
                   },
                   _plugin_groups=[
                       orm.PluginGroup(plugins=[
                           Context(),
                           AccountLoginInit(
                               cfg={
                                   'methods': settings.LOGIN_METHODS,
                                   'get_host_url': settings.get_host_url
                               })
                       ]),
                       orm.PluginGroup(transactional=True,
                                       plugins=[
                                           AccountLoginWrite(),
                                           DeleteCache(cfg=DELETE_CACHE_POLICY)
                                       ])
                   ]),
        orm.Action(id='current_account',
                   skip_csrf=True,
                   arguments={'read_arguments': orm.SuperJsonProperty()},
                   _plugin_groups=[
                       orm.PluginGroup(plugins=[
                           Context(),
                           Read(cfg={'source': 'account.key'}),
                           RulePrepare(),
                           RuleExec(),
                           Set(cfg={'d': {
                               'output.entity': '_account'
                           }}),
                           CallbackExec()
                       ])
                   ]),
        orm.Action(id='read',
                   arguments={
                       'key': orm.SuperKeyProperty(kind='11', required=True),
                       'read_arguments': orm.SuperJsonProperty()
                   },
                   _plugin_groups=[
                       orm.PluginGroup(plugins=[
                           Context(),
                           GetCache(cfg=READ_CACHE_POLICY),
                           Read(),
                           RulePrepare(),
                           RuleExec(),
                           Set(cfg={'d': {
                               'output.entity': '_account'
                           }}),
                           CallbackExec()
                       ])
                   ]),
        orm.Action(
            id='update',
            arguments={
                'key':
                orm.SuperKeyProperty(kind='11', required=True),
                'primary_identity': orm.SuperStringProperty(),
                'disassociate': orm.SuperStringProperty(repeated=True),
                'read_arguments': orm.SuperJsonProperty()
            },
            _plugin_groups=[
                orm.PluginGroup(plugins=[
                    Context(),
                    Read(),
                    AccountUpdateSet(),
                    RulePrepare(),
                    RuleExec()
                ]),
                orm.PluginGroup(
                    transactional=True,
                    plugins=[
                        Write(),
                        DeleteCache(cfg=DELETE_CACHE_POLICY),
                        Set(cfg={'d': {
                            'output.entity': '_account'
                        }}),
                        CallbackExec(cfg=[('callback', {
                            'action_id': 'account_discontinue',
                            'action_model': '31'
                        }, {
                            'account': '_account.key_urlsafe',
                            'account_state': '_account.state'
                        }, lambda account, account_state, **kwargs:
                                           account_state == 'suspended')])
                    ])
            ]),
        orm.Action(
            id='search',
            arguments={
                'search':
                orm.SuperSearchProperty(
                    default={
                        'filters': [],
                        'orders': [{
                            'field': 'created',
                            'operator': 'desc'
                        }]
                    },
                    cfg={
                        'search_arguments':
                        {
                            'kind': '11',
                            'options': {
                                'limit': settings.SEARCH_PAGE
                            }
                        },
                        'filters': {
                            'key':
                            orm.SuperVirtualKeyProperty(kind='11',
                                                        searchable=False),
                            'state':
                            orm.SuperStringProperty(choices=('active',
                                                             'suspended')),
                            'identities.email':
                            orm.SuperStringProperty(searchable=False)
                        },
                        'indexes': [{
                            'orders': [('created', ['asc', 'desc'])]
                        }, {
                            'orders': [('updated', ['asc', 'desc'])]
                        }, {
                            'filters': [('key', ['=='])]
                        }, {
                            'filters': [('identities.email', ['=='])]
                        }, {
                            'filters': [('state', ['=='])],
                            'orders': [('created', ['asc', 'desc'])]
                        }, {
                            'filters': [('state', ['=='])],
                            'orders': [('updated', ['asc', 'desc'])]
                        }]
                    })
            },
            _plugin_groups=[
                orm.PluginGroup(plugins=[
                    Context(),
                    GetCache(cfg={
                        'group': 'admin',
                        'cache': ['admin']
                    }),
                    Read(),
                    RulePrepare(),
                    RuleExec(),
                    Search(),
                    RulePrepare(cfg={'path': '_entities'}),
                    Set(
                        cfg={
                            'd': {
                                'output.entities': '_entities',
                                'output.cursor': '_cursor',
                                'output.more': '_more'
                            }
                        })
                ])
            ]),
        orm.Action(
            id='sudo',
            arguments={
                'key':
                orm.SuperKeyProperty(kind='11', required=True),
                'state':
                orm.SuperStringProperty(required=True,
                                        choices=('active', 'suspended')),
                'message':
                orm.SuperTextProperty(required=True),
                'note':
                orm.SuperTextProperty()
            },
            _plugin_groups=[
                orm.PluginGroup(plugins=[
                    Context(),
                    Read(),
                    Set(
                        cfg={
                            'rm': ['_account.sessions'],
                            'd': {
                                '_account.state': 'input.state'
                            }
                        }),
                    RulePrepare(),
                    RuleExec()
                ]),
                orm.PluginGroup(
                    transactional=True,
                    plugins=[
                        Write(),
                        Set(cfg={'d': {
                            'output.entity': '_account'
                        }}),
                        Notify(
                            cfg={
                                's': {
                                    'subject':
                                    notifications.ACCOUNT_SUDO_SUBJECT,
                                    'body': notifications.ACCOUNT_SUDO_BODY,
                                    'sender': settings.NOTIFY_EMAIL
                                },
                                'd': {
                                    'recipient': '_account._primary_email'
                                }
                            }),
                        DeleteCache(cfg=DELETE_CACHE_POLICY),
                        CallbackExec(cfg=[('callback', {
                            'action_id': 'account_discontinue',
                            'action_model': '31'
                        }, {
                            'account': '_account.key_urlsafe',
                            'account_state': '_account.state'
                        }, lambda account, account_state, **kwargs:
                                           account_state == 'suspended')])
                    ])
            ]),
        orm.Action(
            id='logout',
            arguments={'key': orm.SuperKeyProperty(kind='11', required=True)},
            _plugin_groups=[
                orm.PluginGroup(plugins=[
                    Context(),
                    Read(),
                    Set(cfg={'rm': ['_account.sessions']}),
                    RulePrepare(),
                    RuleExec()
                ]),
                orm.PluginGroup(
                    transactional=True,
                    plugins=[
                        Write(
                            cfg={'dra': {
                                'ip_address': '_account.ip_address'
                            }}),
                        DeleteCache(cfg=DELETE_CACHE_POLICY),
                        AccountLogoutOutput()
                    ])
            ]),
        orm.Action(
            id='blob_upload_url',
            arguments={'upload_url': orm.SuperStringProperty(required=True)},
            _plugin_groups=[
                orm.PluginGroup(plugins=[
                    Context(),
                    Read(),
                    RulePrepare(),
                    RuleExec(),
                    BlobURL(cfg={'bucket': settings.BUCKET_PATH}),
                    Set(cfg={'d': {
                        'output.upload_url': '_blob_url'
                    }})
                ])
            ]),
        orm.Action(id='create_channel',
                   arguments={},
                   _plugin_groups=[
                       orm.PluginGroup(plugins=[
                           Context(),
                           Read(),
                           RulePrepare(),
                           RuleExec(),
                           CreateChannel(),
                           Set(cfg={'d': {
                               'output.token': '_token'
                           }})
                       ])
                   ])
    ]

    def get_output(self):
        dic = super(Account, self).get_output()
        dic.update({
            '_is_guest': self._is_guest,
            '_is_system': self._is_system,
            '_csrf': self._csrf,
            '_root_admin': self._root_admin
        })
        location = self.current_location_data()
        if isinstance(location, dict):
            dic.update(location)
        return dic

    @property
    def _root_admin(self):
        return self._primary_email in settings.ROOT_ADMINS

    @property
    def _is_taskqueue(self):
        return tools.mem_temp_get('current_request_is_taskqueue')

    @property
    def _is_cron(self):
        return tools.mem_temp_get('current_request_is_cron')

    @property
    def _is_system(self):
        return self.key_id_str == 'system'

    @property
    def _is_guest(self):
        return self.key is None

    def primary_email(self):
        self.identities.read()
        if not self.identities.value:
            return None
        for identity in self.identities.value:
            if identity.primary:
                return identity.email

    def get_csrf(self):
        session = self.current_account_session()
        if not session:
            return tools.get_csrf_token()
        return hashlib.md5(
            '%s-%s' % (session.session_id, settings.CSRF_SALT)).hexdigest()

    @classmethod
    def current_account(cls):
        current_account = tools.mem_temp_get('current_account')
        if not current_account:
            current_account = cls()
            cls.set_current_account(current_account)
        return current_account

    @classmethod
    def system_account(cls):
        account_key = cls.build_key('system')
        account = account_key.get()
        if not account:
            identities = [
                AccountIdentity(email='System', identity='1-0', primary=True)
            ]
            account = cls(key=account_key,
                          state='active',
                          identities=identities)
            account._use_rule_engine = False
            account.put()
            account._use_rule_engine = True
        return account

    @classmethod
    def current_account_session(cls):
        return tools.mem_temp_get('current_account_session')

    def session_by_id(self, session_id):
        for session in self.sessions.value:
            if session.session_id == session_id:
                return session
        return None

    def new_session(self):
        account = self
        session_ids = set()
        for session in account.sessions.value:
            if session.created < (datetime.datetime.now() -
                                  datetime.timedelta(days=10)):
                session._state = 'deleted'
            session_ids.add(session.session_id)
        while True:
            session_id = hashlib.md5(tools.random_chars(30)).hexdigest()
            if session_id not in session_ids:
                break
        session = AccountSession(session_id=session_id,
                                 ip_address=self.ip_address)
        account.sessions = [session]
        return session

    @classmethod
    def current_location_data(cls):
        return tools.mem_temp_get('current_request_location_data')

    @classmethod
    def set_location_data(cls, data):
        if data:
            if data.get('_country') and data.get('_country').lower() != 'zz':
                data['_country'] = orm.Key('12', data['_country'].lower())
                if data.get('_region'):
                    data['_region'] = orm.Key(
                        '13',
                        '%s-%s' %
                        (data['_country']._id_str, data['_region'].lower()),
                        parent=data['_country'])
            else:
                data['_region'] = None
                data['_country'] = None
        return tools.mem_temp_set('current_request_location_data', data)

    @classmethod
    def set_taskqueue(cls, flag):
        return tools.mem_temp_set('current_request_is_taskqueue', flag)

    @classmethod
    def set_cron(self, flag):
        return tools.mem_temp_set('current_request_is_cron', flag)

    @classmethod
    def set_current_account(cls, account, session=None):
        tools.mem_temp_set('current_account', account)
        tools.mem_temp_set('current_account_session', session)

    @classmethod
    def set_current_account_from_access_token(cls, access_token):
        try:
            account_key, session_id = access_token.split('|')
        except:
            return False  # Fail silently if the authorization code is not set properly, or it is corrupted somehow.
        if not session_id:
            return False  # Fail silently if the session id is not found in the split sequence.
        account_key = orm.Key(urlsafe=account_key)
        if account_key.kind() != cls.get_kind() or account_key.id(
        ) == 'system':
            return False  # Fail silently if the kind is not valid
        account = account_key.get()
        if account:
            account.read()
            session = account.session_by_id(session_id)
            if session:
                cls.set_current_account(account, session)
                return account