Mikko Ohtamaa <[email protected]> writes:
> Brandon, I like your enthusiasm towards Plone :)
Thanks. :-)
> My rationale ...
> * Web best practices say that friendly URLs are must.
> * @@ does not look friendly in URL.
> * /getpaid-checkout does not look as friendly as /checkout in URL
You're right, "getpaid" does look pretty ugly in a URL.
You've convinced me! Let's go with new, simple URLs like "/checkout".
I'd still love to give shop owners the *option* to move our views "one
level down" in their Plone site, beneath a URL of their own choosing
like "/shop" or whatever, which would also prepare us for web frameworks
like Turbogears and Django that want products to live beneath their own
URL prefix.
How would this work, technically?
Currently, our code does something like this to figure out where the
checkout wizard lives:
portal = getToolByName( self.context, 'portal_url').getPortalObject()
url = portal.absolute_url() + '/@@getpaid-checkout-wizard'
Now, this tells us right off the bat that, as I observed yesterday, the
registration for this view is overly broad:
<browser:page
for="*"
name="getpaid-checkout-wizard"
class=".checkout.CheckoutWizard"
permission="zope2.View"
/>
Since, as the above code demonstrates, we always expect the checkout
wizard to live right beneath the Plone site's main URL, there's no
reason not to tighten the view registration up and say this instead:
for="IStore"
so that every other object in the Plone site doesn't gain a
"@@getpaid-checkout-wizard" view that it doesn't need.
How could we change the assumption built into the above code - that our
views always live on the portal root - so that we also have the option
of living beneath another URL instead? I can think of three ways:
1. We currently slap an "IStore" marker interface on the Plone portal
itself. If we treated it instead as a utility that was merely
registered with the current portal, then it could live anywhere
instead. To find the store, you just ask for the "IStore" utility.
If the site owner is using the default setting, then the portal
itself will have had the "IStore" marker placed on it, and the
checkout page will live at "/Plone/checkout". If the site owner
instead said to put everything under "/shop", then a simple "shop"
object will have been created inside the Plone site and *it* will
have been registered as the "IStore" utility. Under other web
frameworks, we would just jury-rig a global utility registration
pointing at an IStore object that knows where in the Django or
Turbogears URL tree GetPaid has been installed.
2. But maybe it would be a Bad Thing to move the "IStore" marker off of
the portal, since that would both move all of our configuration
views down on to the "/shop" URL instead, if the site owner asked
for one? On the one hand, having the configuration URLs move around
might be not just nice, but necessary, if we think that a TurboGears
integration of GetPaid would re-use our configuration screens, since
they can't there live at the site root very comfortably. But, in
Plone, it seems plain awkward for site-configuration views to live
anywhere else but on the Plone object itself. Plus, a quick search
of the source code suggests that "IStore" is used two ways that are
incompatible with moving it away from the portal root: it's used as
the name for the Store's adapter-and-interface registry where
various GetPaid products register themselves, and the installation
of the IStore marker interface is used as an event trigger by at
least some of the shipping code. So, if all of the above reasons
are good ones for leaving "IStore" alone and keeping it as the name
of the portal root and Zope 3 registration point, then we would want
to create another utility name - maybe "IStoreContext"? - that
serves as the base URL off of which our checkout views live. There
would be three situations:
a. In a default Plone install, the portal root would be both an
IStore and an IStoreContext, making it both where you go to see
configuration views, and also where you go to find the cart and
checkout views.
b. In a Plone install where the owner asks us to live beneath
"/shop", their portal root remains an IStore, and so all of their
Site Setup views would continue to live at places like
"/Plone/@@manage-getpaid-content-types" (note that we allow
ourselves here both the "@@" eyes looking at us and also the name
"getpaid" in the view name, since this URL will be seen, if at
all, only by site admins that expect that sort of thing), but the
"/Plone/shop" object we create is both marked as an IStoreContext
and also registered as the site's IStoreContext utility. That
way, URLs that say "/Plone/shop/checkout" would traverse to the
IStoreContext and find the "checkout" view waiting beneath it,
and checkout code that needs to generate URLs would just ask for
the IStoreContext utility, then ask it for its URL, then slap
"/cart" or whatever on their end.
c. In a non-Zope framework, "/shop" would be configured using the
host's URL registration framework as pointing at a special object
that is itself marked as both an IStore and an IStoreContext, and
that launches Zope traversal when a URL lands on it. Any config
views that survive in the other framework, as well as all of our
customer-facing views, would live under "/shop" in the web site.
We would create a global registry at start-up that advertises our
"shop" object as both the registered IStore utility and
IStoreContext utility.
3. We abandon the modern idea of utility registration for the older
idea we're currently using of merely being able to find our global
options and ask it questions. Recall that at the moment we find
things out about the current payment processor, when that knowledge
is needed, with code like this:
portal = self.context.portal_url.getPortalObject()
manage_options = IGetPaidManagementOptions( portal )
processor_name = manage_options.payment_processor
In other words, instead of asking for a utility, we ask for the
portal root and then forcefully adapt it to the Options interface;
that is, we pretty much insist, through the way we write our code,
that configuration be stored on the site root itself, *not*
somewhere else. I would argue against using such code, since, if
we're not in Plone, (a) there might not exist a site root, and (b)
even if there were, it might not be the best place to put our
configuration information. I like the idea of an IStore utility
that configures us and an IStoreContext utility where our views live
because they are so abstract: they don't insist that we're part of a
Plone site at all, they just require that whatever kind of web
framework we're in be supplied with the ability to provide us with
both of these utilities.
Anyway, as I was saying: if we don't want to use utilities, we could
hang everything off of the ability to get at the global options
object (and, in other web frameworks, somehow fake things so that
something that looked like a portal could be found through something
like the above code). So instead of asking for where the current
IStoreContext is, we could just grab the global options, and ask
them what URL our checkout views should live beneath; and the global
options would have that information because it's through changing
the global options that the store owner expresses their will.
Okay, enough options for right now. :-)
Now, it might be that I'm creating an artificial distinction here
between the "pretty" option of having an IStore and an IStoreContext
utility, and the "ugly" option of grabbing the portal root and making it
adapt to IGetPaidManagementOptions, because there is a third way between
these two extremes: we could register the options themselves as a
utility, and demand that utility as the first act we perform when we
need to learn where GetPaid lives or where it is wired up.
I admit, as I noted in an earlier email, that the idea of starting
*every* question about GetPaid with "go get the options" is very pretty,
because, that way, everything GetPaid needs to know is stored one place.
With my suggestion above that we store *two* different utilities in
every Plone site that uses GetPaid, we start the process of scattering
our configuration information far and wide, where lots of things
(site-wide registrations, for example) have to be changed or cleaned up
when GetPaid is reconfigured or uninstalled.
So, maybe the way to go it to turn the Options into a utility, so that
we can get to it without needing a portal-root to exist, and give *it*
an option that knows where in the site GetPaid is supposed to exist?
Hmm. That wouldn't completely solve the problem, though, since for the
URL "/Plone/shop/checkout" to resolve, there's *got* to be a "shop"
object that actually, really exists in the site. So I'm not sure we can
get away without having *some* site-wide registrations or object
creations going on, unless we give up the idea of Plone users being able
to put our URLs beneath some sub-URL.
And maybe that's an idea we should give up for now, and just try to nail
the two more "obvious" use-cases of Plone with our views living right
beneath the root, and of other web frameworks having us live beneath a
sub-URL, and in both cases we just ask for our Options and find out from
them how to compute our URL?
Hmm.
I should think about this more.
One last option: I think it is *possible*, last I checked, to have views
that (gulp!) live inside of other views. So instead of creating an
actual "shop" *object* in the "/Plone" container of a site owner that
wanted our URLs to live under "/Plone/shop", which would then need to be
cleaned up or deleted later (and which the shop owner would see in their
table of contents and might delete by hand if they didn't know how it
had gotten there), we could register a named view on "IStore" named
"shop" that, itself, has views like "checkout" and "cart" registered for
it. I think that the "vice" RSS syndication system did something like
that to construct interesting multi-part URLs beneath Plone folders
(like ".../recent/rss" and ".../all/atom"), but I'd have to go read
their code to remember how they worked.
--
Brandon Craig Rhodes [email protected] http://rhodesmill.org/brandon
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"getpaid-dev" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/getpaid-dev?hl=en
-~----------~----~----~----~------~----~------~--~---