"The ability to edit articles inside the hub using direct URL or first step to book editor" by Maxim Skripnik on at MaximSkripnik/aboutarticleediting  0 thanks

The ability to edit articles inside the hub using direct URL or first step to book editor

Last update of PillarHub application brought functionality to edit existing article by inserting into browsers URL string full path to it with "/edit" prefix. You can find source code that allows doing it with a detail analysis below.

Editing article by URL

1. HTTP-request handling

Seaside provides a way to catch URL for active application component if you need to use it for some reason. Our reason is simple: find article by its adress that is already visible in the browser URLs string, e.g. if you look on the top of your browser (if you are not using some strange one with URL displayed somewhere else) you will see that path to this article is '/hub/skripnikmaxim/aboutarticleediting'. We only need to get appropriate object-article. For this purpose in class ArticleEditComponent we implemented method initialRequest: which argument aRequest is bassicaly http-request that contains URL that we need. Full method code will be shown later because, in my opinion, it is pretty hard to understand as first sight.

Getting article's path is very simple. You only need to send url message to the argument and then path message to the received result, yielding a sought-for path to article, "splitted" into ordered collection's elements.

aRequest url path -> an OrderedCollection('hub' 'edit' 'maximskripnik' 'aboutarticleediting').

2. Article editing authorization

Of course, it won't be enough to simply move to article, recieved by such way. Otherwise any user who happens to know articles hub and name would be able to freely edit it without even having access to it. It is necessary to control this somehow.

There were implemented such functionallity in PillarHub application already on the hub level. Each hub has its own editors field which stores users who have access to editing its children. The composite pattern on which whole PillarHubs model is built guarantees that each article corresponds to a single hub. The obvious solution is to check if article's "forefather" hub contains current user in the mentioned field.

This check was implemented in new method (rather property) editors in PHArticle class:

^(self forefathers reverse detect: [:forefather | forefather isMemberOf: PHHub ]) editors

forefathers message here answers a collection of all the forefathers of this element (wow).

For any possible changes in applications logic we decided to create a template method hasWriteAccess: in the PHHubElement class which takes some user as an argument aUser and check if it is contained in collection that is returned by method editors that is yet abstract here:

PHArticle>>#hasWriteAccess: aUser
	"template method. 'editors' method is abstract here and should be implemented to return all users who have access to edit element"
	(self editors includes: aUser) ifTrue: [ ^true ] ifFalse: [ ^false ]

3. Hub's owner should always be its editor

During debugging these changes, we found out that before now because of simple uselessness in application hubs owner was never added to set collection of editors when initialized. As a result user, after he wrote an article, would surprisingly notice that he has no access to edit it with the method described here.

To solve this problem we took advantage of existing field personalHub in PHUser hub which indicates its own hub. Obviously, this is exactly what should be used.

In the class PHHub we had to created owner method that, by looking through all the users in SandStoneDB (which is used as a model for storing PillarHubs data) finds and returns such user whos personalHub field refers to current hub:

	^ PHUser find: [:u | u personalHub = self ] ifAbsent: [nil]

Finally, last modification in the model is to add returned value to the collection of editors. Since this collection is an instance of Set class, we won't have any problems due repeated adding so we simply add it before collection return in the method editors:

	"returns set of its hub editors. But to prevent deleting from editors owner of this hub, we always add it in this set"
	editors ifNil: [ editors := Set new ].
	self owner ifNotNil: [ editors add: self owner ].
	^ editors

4. Full code of PHArticleEditComponent>>#initialRequest: method

Now we are done with logic changes. We do identify an article that is requested in URL and we do completely controll access to it. The only thing that is left is to get user from current session, check if he can edit article and, if he really can, change current article (article field) to one that was found from the browser address string.

PHArticleEditComponent>>#initialRequest: aRequest
	| pathToArticle |
	super initialRequest: aRequest.
	self user: self session user.
	pathToArticle := aRequest url path allButFirst: 2.
	((PHRoot default atPath: pathToArticle) hasWriteAccess: self user) ifTrue: [ 
	self article: (PHRoot default atPath: pathToArticle) ].

5. Editing a book in browser through its nested articles

First of all the reason of this change is to allow editing somehow created books' content. By far existing web-interface does not let create books but nevertheless we have an appropriate structure and composite pattern provides the same interface to it as to an article. That's why nothing stops you from manually creating book object in workspace window, then add some articles inside and edit their content, hence the books content, with an existing convenient web-interface simply by using full path to them inserted in the browsers address string.

blog comments powered by Disqus