[Q] CMS/Swiki development

goran.hultgren at bluefish.se goran.hultgren at bluefish.se
Thu Mar 6 08:48:03 UTC 2003


Hi Chris and all!

Chris Burkert <christian.burkert at s2000.tu-chemnitz.de> wrote:
> Göran, (and all the others interested in this discussion)
> 
> goran.hultgren at bluefish.se wrote:
> [snip]
> > HVModule is the thing you instantiate once and register in Comanche. It
> > is a very small class, only has 4 oneliner methods and the method
> > #process: which Comanche likes. It is 6 lines long and sort of
> > kickstarts the url dispatching by creating the top view instance for the
> > model. HVModule holds a "model" and a "prefix". The prefix is used to
> > filter out if the url is meant for this module (so you can have multiple
> > apps running using different prefixes). The model is the top model
> > object for your system.
> 
> So if I have commanche behind an apache this would be a distributor for 
> different domains. For example this is used when apache maps
>    http://www.my-domain-is-longer-than-your-domain.net/
> to
>    comanche:port/my-domain-is-longer-than-your-domain
> and
>    http://www.short-domain.net/
> to
>    comanche:port/short-domain
> . Right?

Yeah, something like that. Apache can map anything, here is an exerpt
from my httpd.conf mapping "hb.bluefish.se" into Comanche:

<VirtualHost 213.212.22.234>
ServerName hb.bluefish.se
ServerAlias hemsidebyggaren.bluefish.se
RewriteEngine on
UseCanonicalName off
ProxyRequests on
NoCache *
ProxyPass /static http://www.bluefish.se/diverse/hb
ProxyPass / http://marvin.bluefish.se:8090/
ProxyPassReverse / http://marvin.bluefish.se:8090/
</VirtualHost>

In the above I am not sure about the need for "RewriteEngine",
"UseCanonicalName", "NoCache" - it was some time ago I set that up - a
lot of copy/paste. The first ProxyPass is used to direct all urls
starting with "static/" to an Apache serving it instead - Apache is
still faster for serving binaries.

But on the other hand - if you are using a recent Comanche (didn't that
fix justgo in? ..or my patched one that actually returns a sensible
cache time for binaries) then they will not be reloaded ALWAYS (like
ComSwiki does - you have noticed the icons at the top always reloading
right?) and then Apache isn't really needed.

I actually don't use the "static" thing currently for hb - I serve
everything using Comanche.

> > HVHttpView is the base class for the view instances that gets created.
> > Currently these are created on every request (initially I toyed with the
> > idea of keeping them around but have not yet needed to). A view has a
> > reference to a model and its methods correspond to the urls - so
> > "calling" an url is like "calling" a method in the view and each
> > "directory" in the url maps to a method in the views. So if you have an
> > url like "/myapp/account/settings/save" - "myapp" is the prefix for the
> > module, "account" is the #account method in the top view. It typically
> > finds the account instance based for the logged in person and creates a
> > view with the account as a model and then continues the dispatch by
> > calling the #settings method in THAT view. That method typically creates
> > a new view on the settings object and calls #save on that, etc.etc. In
> > this way each request creates a chain of HVHttpView (subclasses thereof)
> > instances that can use their "parent" reference if needed.
> 
> If I wanted to dynamically create and delete pages, I had to compile new 
> methods, right?

Nope. Not at all - you would probably do something like this in your
topview class:

page
	"This is the method executed for urls beginning with 'page/'.
	Let's find the correct page, wrap it in a view and dispatch to that
view."

	pageId _ self methodAfter: self prefix, '/page'.
	pageId isEmptyOrNil ifTrue: [^self serverError: 'No page id given in
url.'].
	page := model pageWithId: pageId.
	page ifNil: [^self serverError: 'No page found with id ', pageId].
	method := self methodAfter: self prefix, '/page/', pageId.
	^(page viewFor: self) dispatchOn: method
	
Let us dissect this a bit. The #methodAfter: method is inherited from
HttpView and picks out the next "method" in the url - the next
"directory" in the path so to speak. It is a bit of a cludge having to
repeat the literal '/page' but what the hell. Then we ask the model to
find the page object.

If we find it then we extract the next method, which is the message to
send to the PageView object that we are creating on the last line. So if
the url looks like "http://myswiki.com/appprefix/page/4711/show" then
the var method will be 'show'. Ok, the last line calls #viewFor: on the
page object - this is a double dispatch mechanism to get the correct
view object depending both on the model and the sender (self). The
result will be an instance of PageView with the page as the model. The
method #dispatchOn: will more or less simply call the method #show on
that view object.

Well, there you go! :-)
			
> It would be better for me to register these url-keywords in a dictionary 
> which holds other pageobjects. So for example 'myapp' holds 'account', 
> 'foo' and 'bar'; 'account' holds 'settings' and 'settings' holds 'save'. 
> myapp can give the request to the object at 'account', this can give it 
> to settings and this to save. save answers and the answer goes back in 
> the tree (this is like Morphic ?!?). Only save answers because there is 
> nothing left in the url. If an url doesn't exist there is nil answered 
> and myapp will raise an 404. (self subpages at: 'nonExistingPage' 
> ifAbsent: []).
> 
> This way I didn't have to compile new methods.

I didn't really follow that but HttpView doesn't compile any new
methods. I think you must have misunderstood something - hopefully the
example above cleared it up.

> > HVHtmlBuilder is the object that I use to create XHTML pages. It has a
> > very rich protocol with many different variants for especially Forms.
> > When used it builds an internal collection of objects representing the
> > webpage and in the end it renders this structure on a stream for
> > Comanche to return. Sortof DOM like. The cool thing is that it can
> > autoconnect the posts with the previous request that built the form so
> > that you get the "feeling" of widgets - as you saw in my sample. No ids
> > (they are all autogenerated by the builder) etc.
> 
> This is cool. I'll play with it.

Darn, why am I doing so much PR? Now I must make a new release... :-)
 
> [snip]
> >>I've built my own login class in PHP. It works quite good. (logout after 
> >>idle time, logout after expired, some more things). I want to use the 
> >>same scheme for this.
> > 
> > Sounds like more or less what I do/have.
> 
> I don't want to hassle you with PHP-Code, but if you want it, I can send 
> it to you. 420 lines of code.

Whua! :-) (holding fingers crossed in front of face) Just kidding, you
can send it.
I think my mechanism comes down to much less, but of course I rely on
the session mechanism that Diego added to Comanche (that uses cookies).

> Or just wait. When I finish the first steps of my application it will be 
> SqL and on SqueakMap :-)
> 
> [snip]
> 
> Some other thoughts:
> 
> Each page has the ability to save itself (to Magma, File, whatever).
> 
> There's also a kind of Scriptholder and each page can call Methods of 
> that. So for example (<squeak> printGuestbook </squeak>) the page sends 
> printGuestbook to this Scriptholder and that answers the correct output 
> for this user with these rights. Examples for scripts are:
> - printGuestbook
> - printCalendar
> - printSitemap
> 
> I also think of managing all through some kind of Session that holds 

"Session" is included in Comanche, very easy to use.

> information of the user, the current page and the current permission the 
> user has on this page. This way the Scriptholder could answer depending 
> on the Sessionobject (printing the forms for deleting entrys in the 
> guestbook only when the user has the permission). This Object would be a 
> new instance on every request (holds the GET and POST vars, the USER, 
> PERMISSION and PAGE) and will be deleted after that, just a kind of 
> transfered request. Safety ...

Personally I associate the account object with the session (when logging
in) and all urls that need a login first starts with "/account/...".
Given HttpView this means they will all pass through the #account method
in the top view. That is where I check if you are logged in and if you
are not I will redirect you to the login form. Cool enough I also embed
the intended destination url so that when you log in you will then reach
the url you first requested.

This means that you can bookmark urls "inside" the application, very
nice.

> Corporate Design ... in my PHP-Version the user can only edit a little 
> part of that page and some colors. So it shouldn't be to difficult to 
> create a big template where a few keywords are replaced ({Content}, 
> {Title}, {LastEdit}, {LastUpdateBy}, ...) and the page could answer a 
> local stylesheet with /myapp/foo/bar.stylesheet

Yeah, btw - in HttpView I simply have methods called #css in my view
classes that return the correct CSS. I created a class for CSS so that
Comanche returns the correct mime type.

So in essence the views can return dynamically produced CSS if they
want.

Hmmm, I need to sit down and get all this stuff released...

regards, Göran



More information about the Squeak-dev mailing list