示例#1
0
    def new_password(self):
        """Create a new password

        .. tip::

            Unlike change password this does not demand the old password.
            And hence this method will check in the session for a parameter
            called allow_new_password which has to be True. This acts as a
            security against attempts to POST to this method and changing
            password.

            The allow_new_password flag is popped on successful saving

        This is intended to be used when a user requests for a password reset.
        """
        form = NewPasswordForm(request.form)

        if request.method == "POST" and form.validate():
            if not session.get("allow_new_password", False):
                current_app.logger.debug("New password not allowed in session")
                abort(403)

            self.write(request.nereid_user.id, {"password": form.password.data})
            session.pop("allow_new_password")
            flash(_("Your password has been successfully changed! " "Please login again"))
            session.pop("user")
            return redirect(url_for("nereid.website.login"))

        return render_template("new-password.jinja", password_form=form)
    def get_linkedin_oauth_client(self, site=None, 
            scope='r_basicprofile,r_emailaddress',
            token='linkedin_oauth_token'):
        """Returns a instance of WebCollect

        :param site: Browserecord of the website, If not specified, it will be
                     guessed from the request context
        """
        if site is None:
            site = request.nereid_website

        if not all([site.linkedin_api_key, site.linkedin_api_secret]):
            current_app.logger.error("LinkedIn api settings are missing")
            flash(_("LinkedIn login is not available at the moment"))
            return None

        oauth = OAuth()
        linkedin = oauth.remote_app('linkedin',
            base_url='https://api.linkedin.com',
            request_token_url='/uas/oauth/requestToken',
            access_token_url='/uas/oauth/accessToken',
            authorize_url='/uas/oauth/authenticate',
            consumer_key=site.linkedin_api_key,
            consumer_secret=site.linkedin_api_secret,
            request_token_params={'scope': scope}
        )
        linkedin.tokengetter_func = lambda *a: session.get(token)
        return linkedin
示例#3
0
文件: party.py 项目: baijum/nereid
    def new_password(cls):
        """Create a new password

        .. tip::

            Unlike change password this does not demand the old password.
            And hence this method will check in the session for a parameter
            called allow_new_password which has to be True. This acts as a
            security against attempts to POST to this method and changing
            password.

            The allow_new_password flag is popped on successful saving

        This is intended to be used when a user requests for a password reset.
        """
        form = NewPasswordForm(request.form)

        if request.method == 'POST' and form.validate():
            if not session.get('allow_new_password', False):
                current_app.logger.debug('New password not allowed in session')
                abort(403)

            cls.write(
                [request.nereid_user],
                {'password': form.password.data}
            )
            session.pop('allow_new_password')
            flash(_(
                'Your password has been successfully changed! '
                'Please login again'))
            session.pop('user')
            return redirect(url_for('nereid.website.login'))

        return render_template('new-password.jinja', password_form=form)
    def get_github_oauth_client(self, site=None,
            scope='', token='github_oauth_token'):
        """Returns a instance of LinkedIn OAuth

        :param site: Browserecord of the website, If not specified, it will be
                     guessed from the request context
        """
        if site is None:
            site = request.nereid_website

        if not all([site.github_id, site.github_secret]):
            current_app.logger.error("Github api settings are missing")
            flash(_("Github login is not available at the moment"))
            return None

        oauth = OAuth()
        github = oauth.remote_app('github',
            base_url='https://github.com',
            request_token_url=None,
            access_token_url='/login/oauth/access_token',
            authorize_url='/login/oauth/authorize',
            consumer_key=site.github_id,
            consumer_secret=site.github_secret,
            request_token_params={'scope': scope},
            access_token_method="POST",
        )
        github.tokengetter_func = lambda *a: session.get(token)
        return github
示例#5
0
    def get_linkedin_oauth_client(self,
                                  scope='r_basicprofile,r_emailaddress',
                                  token='linkedin_oauth_token'):
        """Returns a instance of WebCollect

        :param scope: Scope of information to be fetched from linkedin
        :param token: Token for authentication
        """
        if not all([self.linkedin_api_key, self.linkedin_api_secret]):
            current_app.logger.error("LinkedIn api settings are missing")
            flash(_("LinkedIn login is not available at the moment"))
            return None

        oauth = OAuth()
        linkedin = oauth.remote_app(
            'linkedin',
            base_url='https://api.linkedin.com',
            request_token_url='/uas/oauth/requestToken',
            access_token_url='/uas/oauth/accessToken',
            authorize_url='/uas/oauth/authenticate',
            consumer_key=self.linkedin_api_key,
            consumer_secret=self.linkedin_api_secret,
            request_token_params={'scope': scope})
        linkedin.tokengetter_func = lambda *a: session.get(token)
        return linkedin
    def get_github_oauth_client(self,
                                site=None,
                                scope='',
                                token='github_oauth_token'):
        """Returns a instance of LinkedIn OAuth

        :param site: Browserecord of the website, If not specified, it will be
                     guessed from the request context
        """
        if site is None:
            site = request.nereid_website

        if not all([site.github_id, site.github_secret]):
            current_app.logger.error("Github api settings are missing")
            flash(_("Github login is not available at the moment"))
            return None

        oauth = OAuth()
        github = oauth.remote_app(
            'github',
            base_url='https://github.com',
            request_token_url=None,
            access_token_url='/login/oauth/access_token',
            authorize_url='/login/oauth/authorize',
            consumer_key=site.github_id,
            consumer_secret=site.github_secret,
            request_token_params={'scope': scope},
            access_token_method="POST",
        )
        github.tokengetter_func = lambda *a: session.get(token)
        return github
示例#7
0
    def get_facebook_oauth_client(self, site=None):
        """Returns a instance of WebCollect

        :param site: Browserecord of the website, If not specified, it will be
                     guessed from the request context
        """
        if site is None:
            site = request.nereid_website

        if not all([site.facebook_app_id, site.facebook_app_secret]):
            current_app.logger.error("Facebook api settings are missing")
            flash(_("Facebook login is not available at the moment"))
            return None

        oauth = OAuth()
        facebook = oauth.remote_app(
            'facebook',
            base_url='https://graph.facebook.com/',
            request_token_url=None,
            access_token_url='/oauth/access_token',
            authorize_url='https://www.facebook.com/dialog/oauth',
            consumer_key=site.facebook_app_id,
            consumer_secret=site.facebook_app_secret,
            request_token_params={'scope': 'email'})
        facebook.tokengetter_func = lambda *a: session.get(
            'facebook_oauth_token')
        return facebook
示例#8
0
    def new_password(cls):
        """Create a new password

        .. tip::

            Unlike change password this does not demand the old password.
            And hence this method will check in the session for a parameter
            called allow_new_password which has to be True. This acts as a
            security against attempts to POST to this method and changing
            password.

            The allow_new_password flag is popped on successful saving

        This is intended to be used when a user requests for a password reset.
        """
        form = NewPasswordForm(request.form)

        if request.method == 'POST' and form.validate():
            if not session.get('allow_new_password', False):
                current_app.logger.debug('New password not allowed in session')
                abort(403)

            cls.write([request.nereid_user], {'password': form.password.data})
            session.pop('allow_new_password')
            flash(
                _('Your password has been successfully changed! '
                  'Please login again'))
            session.pop('user')
            return redirect(url_for('nereid.website.login'))

        return render_template('new-password.jinja', password_form=form)
示例#9
0
    def get_linkedin_oauth_client(
        self, scope='r_basicprofile,r_emailaddress',
        token='linkedin_oauth_token'
    ):
        """Returns a instance of WebCollect

        :param scope: Scope of information to be fetched from linkedin
        :param token: Token for authentication
        """
        if not all([self.linkedin_api_key, self.linkedin_api_secret]):
            current_app.logger.error("LinkedIn api settings are missing")
            flash(_("LinkedIn login is not available at the moment"))
            return None

        oauth = OAuth()
        linkedin = oauth.remote_app(
            'linkedin',
            base_url='https://api.linkedin.com',
            request_token_url='/uas/oauth/requestToken',
            access_token_url='/uas/oauth/accessToken',
            authorize_url='/uas/oauth/authenticate',
            consumer_key=self.linkedin_api_key,
            consumer_secret=self.linkedin_api_secret,
            request_token_params={'scope': scope}
        )
        linkedin.tokengetter_func = lambda *a: session.get(token)
        return linkedin
示例#10
0
    def get_google_oauth_client(self):
        """
        Returns a instance of WebCollect
        """
        if not all([self.google_app_id, self.google_app_secret]):
            current_app.logger.error("Google api settings are missing")
            flash(_("Google login is not available at the moment"))
            return None

        oauth = OAuth()
        google = oauth.remote_app(
            'google',
            base_url='https://www.google.com/accounts/',
            request_token_url=None,
            access_token_url='https://accounts.google.com/o/oauth2/token',
            access_token_method='POST',
            authorize_url='https://accounts.google.com/o/oauth2/auth',
            consumer_key=self.google_app_id,
            consumer_secret=self.google_app_secret,
            request_token_params={
                'response_type': 'code',
                'scope': 'email',
            },
            access_token_params={'grant_type': 'authorization_code'}
        )
        google.tokengetter_func = lambda *a: session.get('google_oauth_token')
        return google
示例#11
0
    def get_facebook_oauth_client(self, site=None):
        """Returns a instance of WebCollect

        :param site: Browserecord of the website, If not specified, it will be
                     guessed from the request context
        """
        if site is None:
            site = request.nereid_website

        if not all([site.facebook_app_id, site.facebook_app_secret]):
            current_app.logger.error("Facebook api settings are missing")
            flash(_("Facebook login is not available at the moment"))
            return None

        oauth = OAuth()
        facebook = oauth.remote_app('facebook',
            base_url='https://graph.facebook.com/',
            request_token_url=None,
            access_token_url='/oauth/access_token',
            authorize_url='https://www.facebook.com/dialog/oauth',
            consumer_key=site.facebook_app_id,
            consumer_secret=site.facebook_app_secret,
            request_token_params={'scope': 'email'}
        )
        facebook.tokengetter_func = lambda *a: session.get(
                'facebook_oauth_token'
        )
        return facebook
    def get_linkedin_oauth_client(self,
                                  site=None,
                                  scope='r_basicprofile,r_emailaddress',
                                  token='linkedin_oauth_token'):
        """Returns a instance of WebCollect

        :param site: Browserecord of the website, If not specified, it will be
                     guessed from the request context
        """
        if site is None:
            site = request.nereid_website

        if not all([site.linkedin_api_key, site.linkedin_api_secret]):
            current_app.logger.error("LinkedIn api settings are missing")
            flash(_("LinkedIn login is not available at the moment"))
            return None

        oauth = OAuth()
        linkedin = oauth.remote_app(
            'linkedin',
            base_url='https://api.linkedin.com',
            request_token_url='/uas/oauth/requestToken',
            access_token_url='/uas/oauth/accessToken',
            authorize_url='/uas/oauth/authenticate',
            consumer_key=site.linkedin_api_key,
            consumer_secret=site.linkedin_api_secret,
            request_token_params={'scope': scope})
        linkedin.tokengetter_func = lambda *a: session.get(token)
        return linkedin
示例#13
0
    def recent_products(self):
        """
        GET
        ---

        Return a list of recently visited products in JSON

        POST
        ----

        Add the product to the recent list manually. This method is required
        if the product page is cached, or is served by a Caching Middleware
        like Varnish which may clear the session before sending the request to
        Nereid.

        Just as with GET the response is the AJAX of recent products
        """
        if request.method == 'POST':
            self._add_to_recent_list(request.form.get('product_id', type=int))

        fields = request.args.getlist('fields')
        if fields:
            allowed_fields = [
                f for f in fields if f in self.json_allowed_fields
            ]
        else:
            allowed_fields = self.json_allowed_fields[:]
        products = []

        if 'sale_price' in allowed_fields:
            allowed_fields.remove('sale_price')

        if hasattr(session, 'sid'):
            product_ids = session.get('recent-products', [])
            products = self.read(product_ids, allowed_fields)
            for product in products:
                product['sale_price'] = format_currency(
                        self.sale_price(product['id']),
                        request.nereid_currency.code
                )

        return jsonify(
            products = products
        )
示例#14
0
    def get_github_oauth_client(self, scope='', token='github_oauth_token'):
        """Returns a instance of Github OAuth
        """
        if not all([self.github_id, self.github_secret]):
            current_app.logger.error("Github api settings are missing")
            flash(_("Github login is not available at the moment"))
            return None

        oauth = OAuth()
        github = oauth.remote_app('github',
            base_url='https://github.com',
            request_token_url=None,
            access_token_url='/login/oauth/access_token',
            authorize_url='/login/oauth/authorize',
            consumer_key=self.github_id,
            consumer_secret=self.github_secret,
            request_token_params={'scope': scope},
            access_token_method="POST",
        )
        github.tokengetter_func = lambda *a: session.get(token)
        return github
示例#15
0
    def recent_products(cls):
        """
        GET
        ---

        Return a list of recently visited products in JSON

        POST
        ----

        Add the product to the recent list manually. This method is required
        if the product page is cached, or is served by a Caching Middleware
        like Varnish which may clear the session before sending the request to
        Nereid.

        Just as with GET the response is the AJAX of recent products
        """
        if request.method == 'POST':
            cls._add_to_recent_list(request.form.get('product_id', type=int))

        fields = set(request.args.getlist('fields')) or cls.json_allowed_fields
        fields = fields & cls.json_allowed_fields

        if 'sale_price' in fields:
            fields.remove('sale_price')

        response = []
        if hasattr(session, 'sid'):
            products = cls.browse(session.get('recent-products', []))
            for product in products:
                product_val = {}
                for field in fields:
                    product_val[field] = getattr(product, field)
                product_val['sale_price'] = format_currency(
                    product.sale_price(),
                    current_locale.currency.code
                )
                response.append(product_val)

        return jsonify(products=response)
示例#16
0
    def recent_products(cls):
        """
        GET
        ---

        Return a list of recently visited products in JSON

        POST
        ----

        Add the product to the recent list manually. This method is required
        if the product page is cached, or is served by a Caching Middleware
        like Varnish which may clear the session before sending the request to
        Nereid.

        Just as with GET the response is the AJAX of recent products
        """
        if request.method == 'POST':
            cls._add_to_recent_list(request.form.get('product_id', type=int))

        fields = set(request.args.getlist('fields')) or cls.json_allowed_fields
        fields = fields & cls.json_allowed_fields

        if 'sale_price' in fields:
            fields.remove('sale_price')

        response = []
        if hasattr(session, 'sid'):
            products = cls.browse(session.get('recent-products', []))
            for product in products:
                product_val = {}
                for field in fields:
                    product_val[field] = getattr(product, field)
                product_val['sale_price'] = format_currency(
                    product.sale_price(),
                    request.nereid_currency.code
                )
                response.append(product_val)

        return jsonify(products=response)
示例#17
0
    def get_facebook_oauth_client(self):
        """
        Returns a instance of WebCollect
        """
        if not all([self.facebook_app_id, self.facebook_app_secret]):
            current_app.logger.error("Facebook api settings are missing")
            flash(_("Facebook login is not available at the moment"))
            return None

        oauth = OAuth()
        facebook = oauth.remote_app(
            "facebook",
            base_url="https://graph.facebook.com/",
            request_token_url=None,
            access_token_url="/oauth/access_token",
            authorize_url="https://www.facebook.com/dialog/oauth",
            consumer_key=self.facebook_app_id,
            consumer_secret=self.facebook_app_secret,
            request_token_params={"scope": "email"},
        )
        facebook.tokengetter_func = lambda *a: session.get("facebook_oauth_token")
        return facebook
示例#18
0
    def get_facebook_oauth_client(self):
        """
        Returns a instance of WebCollect
        """
        if not all([self.facebook_app_id, self.facebook_app_secret]):
            current_app.logger.error("Facebook api settings are missing")
            flash(_("Facebook login is not available at the moment"))
            return None

        oauth = OAuth()
        facebook = oauth.remote_app(
            'facebook',
            base_url='https://graph.facebook.com/',
            request_token_url=None,
            access_token_url='/oauth/access_token',
            authorize_url='https://www.facebook.com/dialog/oauth',
            consumer_key=self.facebook_app_id,
            consumer_secret=self.facebook_app_secret,
            request_token_params={'scope': 'email'})
        facebook.tokengetter_func = lambda *a: session.get(
            'facebook_oauth_token')
        return facebook
示例#19
0
    def open_cart(cls, create_order=False):
        """Logic of this cart functionality is inspired by amazon. Most
        e-commerce systems handle cart in a different way and it is important
        to know how the cart behaves under different circumstances.

        :param create_order: If `True` Create a sale order and attach
            if one does not already exist.
        :return: The Active record for the shopping cart of the user

        The method is guaranteed to return a cart but the cart may not have
        a sale order. For methods like add to cart which definitely need a sale
        order pass :attr: create_order = True so that an order is also assured.
        """
        Sale = Pool().get("sale.sale")
        NereidUser = Pool().get("nereid.user")

        # request.nereid_user is not used here this method is used by the
        # signal handlers immediately after a user logs in (but before being
        # redirected). This causes the cached property of nereid_user to remain
        # in old value through out the request, which will not  have ended when
        # this method is called.
        user = NereidUser(session.get("user", request.nereid_website.guest_user.id))

        # for a registered user there is only one cart, session is immaterial
        if "user" in session:
            carts = cls.search(
                [("sessionid", "=", False), ("user", "=", user.id), ("website", "=", request.nereid_website.id)]
            )
        else:
            carts = cls.search(
                [("sessionid", "=", session.sid), ("user", "=", user.id), ("website", "=", request.nereid_website.id)],
                limit=1,
            )

        if not carts:
            # Create a cart since it definitely does not exists
            carts = [
                cls.create(
                    {
                        "user": user.id,
                        "website": request.nereid_website.id,
                        "sessionid": session.sid if "user" not in session else None,
                    }
                )
            ]

        cart, = carts

        if cart.sale:
            cart.sanitise_state(user)

        # Check if the order needs to be created
        if create_order and not cart.sale:
            # Try any abandoned carts that may exist if user is registered
            sale_orders = (
                Sale.search(
                    [
                        ("state", "=", "draft"),
                        ("is_cart", "=", True),
                        ("website", "=", request.nereid_website.id),
                        ("party", "=", user.party.id),
                        ("currency", "=", request.nereid_currency.id),
                    ],
                    limit=1,
                )
                if "user" in session
                else None
            )
            cls.write([cart], {"sale": sale_orders[0].id if sale_orders else cls.create_draft_sale(user).id})

        return cls(cart.id)
示例#20
0
    def sign_in(cls):
        '''
        Step 1: Sign In or Register

        GET
        ~~~

        Renders a sign-in or register page. If guest checkout is enabled, then
        an option to continue as guest is also permitted, in which case the
        email is a required field.

        POST
        ~~~~

        For guest checkout, this sign in would create a new party with the name
        as the current session_id and move the shopping cart's sale to the
        new user's ownership

        Designer notes: The registration or login must contact the
        corresponding handlers. Login and Registraion handlers are designed to
        handle a `next` parameter where the user would be redirected to if the
        operation was successful. The next url is provided in the context

        OTOH, if the user desires to checkout as guest, the user is required to
        fill in the email and submit the form, which posts the email to this
        handler.
        '''
        NereidCart = Pool().get('nereid.cart')
        NereidUser = Pool().get('nereid.user')
        Party = Pool().get('party.party')

        if not current_user.is_anonymous:
            form = cls.sign_in_form(
                email=current_user.email,
                checkout_mode='account',
            )
        else:
            # Guest user
            form = cls.sign_in_form(
                email=session.get('email'),
                checkout_mode='guest',
            )

        if form.validate_on_submit():
            if form.checkout_mode.data == 'guest':

                if not cls.allowed_as_guest(form.email.data):
                    return render_template(
                        'checkout/signin-email-in-use.jinja',
                        email=form.email.data)

                cart = NereidCart.open_cart()
                party_name = unicode(
                    _('Guest with email: %(email)s', email=form.email.data))
                if cart.sale.party == current_website.guest_user.party:
                    # Create a party with the email as email, and session as
                    # name, but attach the session to it.
                    party, = Party.create([{
                        'name':
                        party_name,
                        'nereid_session':
                        session.sid,
                        'addresses': [],
                        'contact_mechanisms': [('create', [{
                            'type':
                            'email',
                            'value':
                            form.email.data,
                        }])]
                    }])

                    cart.sale.party = party
                    # TODO: Avoid this if the user comes to sign-in twice.
                    cart.sale.shipment_address = None
                    cart.sale.invoice_address = None
                    cart.sale.save()
                else:
                    # Perhaps the email changed ?
                    party = cart.sale.party
                    party.name = party_name

                    # contact_mechanism of email type will always be there for
                    # Guest user
                    contact_mechanism = filter(lambda c: c.type == 'email',
                                               party.contact_mechanisms)[0]
                    contact_mechanism.value = form.email.data
                    contact_mechanism.save()
                    party.email = form.email.data
                    party.save()

                return redirect(url_for('nereid.checkout.shipping_address'))
            else:
                # The user wants to use existing email to login
                user = NereidUser.authenticate(form.email.data,
                                               form.password.data)
                if user:
                    # FIXME: Remove remember_me
                    login_user(user, remember=form.remember.data)
                    return redirect(
                        url_for('nereid.checkout.shipping_address'))
                else:
                    failed_login.send()

        if not current_user.is_anonymous:
            # Registered user with a fresh login can directly proceed to
            # step 2, which is filling the shipping address
            #
            # if this is a recent sign-in by a registred user
            # automatically proceed to the shipping_address step
            return redirect(url_for('nereid.checkout.shipping_address'))

        return render_template(
            'checkout/signin.jinja',
            form=form,
            next=url_for('nereid.checkout.shipping_address'))
示例#21
0
    def sign_in(cls):
        '''
        Step 1: Sign In or Register

        GET
        ~~~

        Renders a sign-in or register page. If guest checkout is enabled, then
        an option to continue as guest is also permitted, in which case the
        email is a required field.

        POST
        ~~~~

        For guest checkout, this sign in would create a new party with the name
        as the current session_id and move the shopping cart's sale to the
        new user's ownership

        Designer notes: The registration or login must contact the
        corresponding handlers. Login and Registraion handlers are designed to
        handle a `next` parameter where the user would be redirected to if the
        operation was successful. The next url is provided in the context

        OTOH, if the user desires to checkout as guest, the user is required to
        fill in the email and submit the form, which posts the email to this
        handler.
        '''
        NereidCart = Pool().get('nereid.cart')
        NereidUser = Pool().get('nereid.user')
        Party = Pool().get('party.party')

        if not current_user.is_anonymous():
            form = cls.sign_in_form(
                email=current_user.email,
                checkout_mode='account',
            )
        else:
            # Guest user
            form = cls.sign_in_form(
                email=session.get('email'),
                checkout_mode='guest',
            )

        if form.validate_on_submit():
            if form.checkout_mode.data == 'guest':

                if not cls.allowed_as_guest(form.email.data):
                    return render_template(
                        'checkout/signin-email-in-use.jinja',
                        email=form.email.data
                    )

                cart = NereidCart.open_cart()
                party_name = unicode(_(
                    'Guest with email: %(email)s', email=form.email.data
                ))
                if cart.sale.party == request.nereid_website.guest_user.party:
                    # Create a party with the email as email, and session as
                    # name, but attach the session to it.
                    party, = Party.create([{
                        'name': party_name,
                        'nereid_session': session.sid,
                        'addresses': [],
                        'contact_mechanisms': [('create', [{
                            'type': 'email',
                            'value': form.email.data,
                        }])]
                    }])

                    cart.sale.party = party
                    # TODO: Avoid this if the user comes to sign-in twice.
                    cart.sale.shipment_address = None
                    cart.sale.invoice_address = None
                    cart.sale.save()
                else:
                    # Perhaps the email changed ?
                    party = cart.sale.party
                    party.name = party_name

                    # contact_mechanism of email type will always be there for
                    # Guest user
                    contact_mechanism = filter(
                        lambda c: c.type == 'email', party.contact_mechanisms
                    )[0]
                    contact_mechanism.value = form.email.data
                    contact_mechanism.save()
                    party.email = form.email.data
                    party.save()

                return redirect(
                    url_for('nereid.checkout.shipping_address')
                )
            else:
                # The user wants to use existing email to login
                user = NereidUser.authenticate(
                    form.email.data, form.password.data
                )
                if user:
                    # FIXME: Remove remember_me
                    login_user(user, remember=form.remember.data)
                    return redirect(
                        url_for('nereid.checkout.shipping_address')
                    )
                else:
                    failed_login.send()

        if not current_user.is_anonymous():
            # Registered user with a fresh login can directly proceed to
            # step 2, which is filling the shipping address
            #
            # if this is a recent sign-in by a registred user
            # automatically proceed to the shipping_address step
            return redirect(url_for('nereid.checkout.shipping_address'))

        return render_template(
            'checkout/signin.jinja',
            form=form,
            next=url_for('nereid.checkout.shipping_address')
        )