betabug... Sascha Welter

home english | home deutsch | Site Map | Sascha | Kontakt | Pro | Weblog | Wiki

Entries : Category [ zope ]
All around the Zope application server
[digital]  [language]  [life]  [security]  [media]  [zope]  [tourism]  [limnos]  [mac]  [athens]  [travel]  [montage]  [food]  [fire]  [zwiki]  [schnipsel]  [music]  [culture]  [shellfun]  [photography]  [hiking]  [pyramid]  [politics]  [bicycle]  [naxos]  [swim] 

12 October 2007

Comments Open Again

Akismet for spam protection
 

After having been forced to have all comments pre-moderated on my weblog for some time now, I've installed kedai's akizmet plugin. This will now run comments through the akismet service [1] to check them for their "spamminess". My setup is slightly different from the one kedai described, more on that in a later post.

Result: You can now comment immediately on this blog, I don't have to approve all comments, spammers should not be able to dump their ugly stuff here. Yeah, well, we're still in the testing phase, so it might not always work out, YMMV, etc. etc. ... :-)

[1]note subtly different spelling, 's' vs. 'z'

Posted by betabug at 17:11 | Comments (2) | Trackbacks (0)
17 October 2007

http:BL

Block me with a 403
 

Monday evening I thought about yet another way of making life on my site difficult for comment spammers / email harvesters. I attempted to get a Zope product that would make lookups against the Project Honeypot http:BL blacklist ...and I got that one up and running in about 1.2 hours. In the evening it was basically working. Spent some more hours on Tuesday for writing tests and cleaning up.

Right now it's working/testing on my blog, and some bots already got a bloody nose and a 403. Need to write up some more documentation and then it's time for a first release, even if parts of the functionality are still a bit rough, especially towards changing the policy and glueing in with Zope CMS products. Coming soon at an INSTANCE_HOME near you!

So, what's it doing? It's looking up every visitors IP against a DNS blacklist, much like a lot of SMTP servers are doing nowadays. If the visitors IP is listed in the Project Honeypot database, a simple policy decides to let the visitor view pages or reject them with a 403 "forbidden" HTTP response. There are already a couple of modules around to do these lookups, e.g. for apache 2. My reason for coding this up in Zope is that a.) I don't run apache 2 and b.) I'm not sure that every request needs to be looked up, e.g. stylesheets, images, etc. are not requested by "my" bots anyway.

Update 2007-10-24: The code is released now!


Posted by betabug at 10:16 | Comments (3) | Trackbacks (0)
24 October 2007

http:BL for Zope released!

Look them up and block them...
 

Finally I have grinded through my ToDo list and yesterday late at night I came to the point where I can reasonably release a first version of my HoneyPotBL Zope product. This is a product for Zope 2 that will let you query the http:BL DNS Blacklist of known email scrapers / harvesters, comment spammers, and other malicious bots that the Project Honeypot is providing.

This release features: Working code that you can integrate into your Zope CMS / Zope app to look up IPs against the http:BL DNS blacklist and then block malicious bots with a 403 status code (or alternatively just block them from posting comments, give them alternate content... you decide). This release is in production use on my own site [1]. Whitelisting human visitors who find themselves by accident on the wrong side of a 403 return code can temporarily whitelist themselves and get access to your site. You decide on what policy to follow here. Documentation there's a README and some example glue code for integrating into your code. Tests - some basic unit tests are provided. Developer Access: If you want to hack on this, you can check out the dev version from the darcs repository, and then hack and patch on your own repository to your hearts content (who knows, you might even want to send me patches :-).

Please find the release page (with download link) on my shiny new wiki.

[1]Hey, it just checked your IP when you visited this page!

Posted by betabug at 10:21 | Comments (0) | Trackbacks (1)
25 October 2007

vi temp file + Zope webdav setup

Just a "note to self" post

These settings in ~/.exrc (or ~/.vimrc) will keep a lot of vi (or vim) temp files out of the way when accessing a Zope database over WebDAV:

" zope / webdav stuff, avoids creating new files on writing
set nobackup
set nowritebackup
" keep swapfiles in my home directory
set dir=~/.tmp

(Note to others: Accessing a Zope database over WebDAV works fine with the built in Mac OS X WebDAV client, dunno about others. I use it over https.)


Posted by betabug at 19:21 | Comments (0) | Trackbacks (0)
30 October 2007

Quasi-Normal in Numbers

How much is that?

Zope programmers learn sooner or later that persistent objects don't like dictionaries and lists as attributes. Why? Because to keep their values around you have to assign them back to the object - and that will write a new revision compromising all of the object to the ZODB. Which wastes space and can lead to more ConflictErrors. But how much space? Yesterday evening I found one such case in Zwiki and in moving the dictionary in question to a BTree, I wrote down some numbers, in the process also rediscovering the ZCatalog in there...


Zwiki visitors can "rate" pages with 1-5 stars. The code in question stored the personal rating in a dictionary on the page. At first there's nothing bad about that, there are not so many ratings by so many people anyway. But the mentioned unpersistence on dictionary attributes led me to rewrite the code. The diff will soon be in the Zwiki darcs repository, so I'm not pasting it here. It's not the insteresting part. Let's look instead at the Data.fs growth, measured by a simple ls -l.

All numbers are in bytes. For reference I've rated pages from two browsers, one with a logged in user, on with a quasi-anonymous user with a user name set in a cookie. I've always double checked that simple page loads won't trigger stuff that grows the Data.fs, but otherwise things might not be very scientific.

comparison

For comparison, I've looked at some simple actions on a Zwiki page: Adding a comment and a visitor setting their name in the "options" page. Obviously the Data.fs growth of saving a comment is highly dependant of the size of the page and the comment.

comment add: ~ 26651
saving name in options: 970

As I was checking the numbers for the old code, quickly I discovered that some of the growth is due to the ZCatalog getting fed too. We can measure that effect.

old code

voting: 8755
voting from other account: 8568
voting on big page: 20264

old code without catalog reindex

voting: 5899
voting from other account: 5896
voting on big page: 17396
another vote on big page: 17414

Some of the growth clearly is coming from reindexing the object in the catalog. But the main observation here is that voting on a big page results in more bytes being written to Data.fs, since we are still writing down all of the object for each vote.

new code

voting: 2556
voting from other account: 2553
voting on big page: 2949
another vote on big page: 2528

Here I have rewritten the code to store ratings in an OOBTree. The migration of dictionary to BTree is not reflected in the numbers - that has to be done just once anyway, not for each rating being registered. We already see some reduction here, but the main observation is again on the difference of the "normal" page (more or less a default Zwiki FrontPage) to a slightly bigger page: There are in fact less bytes written when voting on the big page now, obviously we do not write all of the object to the ZODB any more. But then we still write a lot of bytes for such a small vote. Where does it all go?

new code without catalog reindex

voting: 169
voting from other account: 166
voting on big page: 169
another vote on big page: 166

Getting rid of reindexing for a moment and... we get very reasonable numbers all of a sudden. I can imagine a bit of overhead for writing objects to an OODB, so writing 169 bytes for a vote looks reasonable. You can even see that the 2nd accounts username is a bit shorter than the 1st. Lesson learned here: If you don't need to index your object in the ZCatalog, don't.

Reindexing all of the indexes in the ZCatalog is some overhead, but we do not change so much on the object. So why not just reindex only those indexes which we actually changed on our object?

new code reindexing only 2 indexes

voting: 2365
voting from other account: 2362
voting on big page: 2340
another vote on big page: 2337

The code for this looks basically like this:

catalog.catalog_object(object_that_changed, idxs=['rating', 'voteCount'])

We are getting down a bit again, but only slightly so. What might be the reason?

new code reindexing only 2 indexes, without metadata

voting: 608
voting from other account: 614
voting on big page: 591
another vote on big page: 514

Where we have used an optional parameter on the same line:

catalog.catalog_object(self, idxs=['rating', 'voteCount'], \
                                            update_metadata=0)

Here we told the catalog update to not update metadata. Obviously in practice this might not be a good choice for our code, as index and metadata diverge now, but it can show us something in the numbers: They are very reasonable now, we're writing a few hundred bytes, but we've got updated index and an updated object. Lesson learned here No. 1: If you don't need metadata in your ZCatalog, don't put it there.

Oh, and Lesson learned here No. 2: Even though the idxs parameter on the catalog_object() will update only the specified indexes, the call still will update all the metadata. There is a comment in the Zope code (in Products/ZCatalog/Catalog.py on updateMetadata()) "Given an object and a uid, update the column data for the uid with the object data if the object has changed" which could be mistaken that only the changed metadata is updated, but indeed all the metadata seems to be rewritten (will need to grok that particular piece of code more).

Conclusions

For the moment we will go for the "new code reindexing only 2 indexes" version in Zwiki. BTrees are nice. Cataloging stuff is a tradeoff.

Back to our quasi-normal state of hacking, or as Saad reports about the strike in Paris:

Le trafic aérien à Air France devrait reprendre de façon quasi-normale...

Posted by betabug at 11:34 | Comments (0) | Trackbacks (0)
06 November 2007

MiniPlanet, a Mini Feed Aggregator for Zope

Released fresh from the tar presses!
 

Here it is: a minimal planet for collecting the RSS (and atom) feeds of blogs and other sites you are reading, and for showing them off to your visitors on your Zope site. Want to see an example? Just look at the lower right corner of my weblogs main page, there it is.

This thing has been in the making for a while, but that just means it's already in production use for a while too (here and recently on Wu's blog too). In the last week or so I've cleaned it up, added a couple more features and tests and release documentation.

The code tries to write to the ZODB as little as possible. It uses BTrees for almost everything, follows If-modifed-since headers (actually it uses feedparser, and feedparser does that). It's also optimized to use only very little screen space... it's a mini planet.

Get it from the MiniPlanet release page (with download link) on my wiki!


Posted by betabug at 20:51 | Comments (4) | Trackbacks (0)
13 November 2007

Stupid apache adds Content-type to 304 Replies

Now that we fixed it in Zope...

Last weekend I upgraded my Zope to profit from the bugfix for the 304 responses should not have Content-Length header issue. Funny enough, my pages were still setting Content-type headers for those empty 304 responses. I was ready to blame Zope, until I noticed that my local test instance didn't do it. In fact Zope doesn't do this at all, but as soon as you place it behind apache, apache 1.3 will happily add the DefaultType content-type to empty replies. Don't believe me? It's easy to try it yourself. Also these tomcat people noticed the same thing - they seem to disagree on the reading of the RFC though.

So what do I do now with the Zwiki code that handles "If-modified-since" headers and 304 replies? Add the header back in, f* the RFC? What a mess. It wouldn't be so bad if some proxy servers and Safari didn't mess it up when they get a 304 with text/plain all of a sudden on a locally cached page.


Posted by betabug at 21:27 | Comments (1) | Trackbacks (0)
04 December 2007

Suppress zLOG output in Zope tests

Another of these "note to self" posts
 

In an old product of mine, the ZopeTestCase tests were being run from a shell script, using framework.py. I'm switching that to running them with zopectl test. It's not too hard after I had found out how. But strange enough it would show any zLOG output of level WARNING or higher on the console. Annoying when all you want to see is a happy line of ......... test dots. So here is how to suppress this log output...


Digging through the web to find the solution didn't help much, but today I dived into the source and discovered that all I had to do was to set up a log.ini file to configure the python logger.

I took the log.ini which I found in the Zope source tarball, and changed it to log to a special place where all WARNINGs are kept in good company and get served nice cups of tea once in a while:

[handler_normal]
class=FileHandler
level=NOTSET
formatter=common
args=('/dev/null', 'a')
...

Of course sometimes it might be necessary to actually see WARNING or other error log messages, in which case I will have to rename log.ini. A command line switch would be nice, but the --nowarnings mentioned in the docs doesn't seem to have any effect in this. This is Zope 2.9 by the way.

I didn't bother to understand the fine points of the log.ini format. I guess it's there for those people who need something special or those who have the time to cut the leaves on their bonsai trees every morning.

Posted by betabug at 13:54 | Comments (2) | Trackbacks (0)
23 December 2007

Fix Functional Tests in Zope 2.10

Copy + Paste, my friend

A few weeks ago I introduced a new bug into the Zwiki code. A bug which made it past our tests. So I made up my mind to finally add some high level functional tests to our arsenal. Been there, done that, it's not so difficult. Then I hit the error TraversalError('No traversable adapter found', obj). It seems that the Zope 3 Page Template implementation in Zope 2.10 made it impossible to run the same functional tests on Zope 2.9 and 2.10. Which is kind of unfortunate if you happen to work on a Product that should run under multiple Zope versions. The solution is to copy+paste about 10 lines of code...


Searching the web I discovered a couple of hints at what I had to do:

In the end I had to combine philiKON's basic line, with the imports stolen from Łukasz, then I brewed it up into the form of Martin's setup (but changed to a more "normal" test setup).

Before I go through the solution in detail, I want to deliver a little rant to the Zope developers: I'm all for building modern stuff, modern programming paradigms, less boilerplate code, code reuse, etc. But (you knew there was a "but" coming) if the new stuff breaks existing code and the solution is just to copy and paste some boilerplate code that I don't even have to understand and that has not a snowball-in-hell chance to ever need to be changed / adjusted / customized, then... this new code stuff of yours just [choose your prefered expletive here] and should be considered broken.

The copy+pasted fix I'm applying here should probably be put into 2.10's ZopeTestCase itself, because then I wouldn't have the situation that my code works with only either 2.9 or 2.10 - I could leave my code alone and it would work on both Zopes (well, if I hadn't chosen to go with zope.testbrowser which isn't in 2.9's Five). But is this really the result of shiny new Interfaces, and IThis and IThat, that I have to copy+paste some blabla-code somewhere? End of rant.

So, let's go through the setup:

import unittest
import doctest

from Testing import ZopeTestCase
ZopeTestCase.installProduct('ZCatalog')
ZopeTestCase.installProduct('ZWiki')

# ================== add these ========================
# imports copied from Lukasz mostly
try:
    from zope import traversing, component, interface
except ImportError:
    print '--------------------------------------------'
    print 'Functional tests will only run in Zope 2.10+'
    print '--------------------------------------------'
    raise
from zope.traversing.adapters import DefaultTraversable
from zope.traversing.interfaces import ITraversable
from zope.component import provideAdapter
from zope import interface
from zope.interface import implements
# ================== back to normal setup =============

class TestZWikiFunctional(ZopeTestCase.FunctionalTestCase):
    """
    Testing browser paths through ZWiki.
    """
    # ================== add these ========================
    implements(ITraversable)

    def beforeSetUp(self):
        super(ZopeTestCase.FunctionalTestCase, self).beforeSetUp()
        component.provideAdapter( \
            traversing.adapters.DefaultTraversable, \
            (interface.Interface,),ITraversable)
    # ================== till here ========================

    def testSomething(self):
        # This is here, because otherwise beforeSetUp wouldn't be run
        # it's only necessary because all my tests are in the
        # FunctionalDocFileSuite - which is added afterwards
        pass

    # old-style functional tests would go here

def test_suite():
    suite = unittest.makeSuite(TestZWikiFunctional)
    # the following is only when you go with zope.testbrowser tests:
    suite.addTest(ZopeTestCase.FunctionalDocFileSuite(
            'functional.txt', package='Products.ZWiki',
            optionflags=doctest.REPORT_ONLY_FIRST_FAILURE |
                        doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS))
    return suite

if __name__ == "__main__":
    unittest.main(defaultTest='test_suite')

So, if I count this right, I have to add about 10 lines of boilerplate, never to be changed code to get Functional tests running on Zope 2.10 again. This seems to be only if your Functional tests depend on Page Templates - but of course which Functional tests wouldn't depend on Page Templates?

The end result (the complete tests with the files functional.txt and Functional_tests.py) will hopefully soon turn up in ZWiki source code, for the moment these links point to my private repo.

As a final note I want to say that I really enjoyed working with zope.testbrowser. It's fun to write tests like that, even though mine aren't perfect yet.

Posted by betabug at 13:01 | Comments (0) | Trackbacks (0)
11 January 2008

Who's Pickling Who?

A python hunt
 

Today I was faced with an error message of "TypeError: can't pickle instancemethod objects" and a traceback that was unusual in that it didn't reference my code at all (see below). Peterbe recently posted a similar problem to the Zope list, but in his case this happened only in a test case. For me it was right inside my code. I found another mention on the python list, which pointed me to the right direction, namely assigning a method as an attribute to an object, when I thought I was assigning a string...


Well, first of all the traceback, which doesn't tell me anything, as it doesn't reference my code at all:

Module ZPublisher.Publish, line 121, in publish
Module Zope2.App.startup, line 240, in commit
Module transaction._manager, line 96, in commit
Module transaction._transaction, line 380, in commit
Module transaction._transaction, line 378, in commit
Module transaction._transaction, line 433, in _commitResources
Module ZODB.Connection, line 484, in commit
Module ZODB.Connection, line 526, in _commit
Module ZODB.Connection, line 553, in _store_objects
Module ZODB.serialize, line 407, in serialize
Module ZODB.serialize, line 416, in _dump
Module copy_reg, line 69, in _reduce_ex

TypeError: can't pickle instancemethod objects

This doesn't really tell me where in my code the problem was. Also sprinkling the code with print statements didn't help either, since the error occurs at the transaction commit, so all the print statements are executed. So my task was to "divide and conquer" another way: I knew which method was called in the browser and I started to break it up by inserting a simple return 'done' in the middle. Moving this line back and forth I located the line that was producing the error.

In my code I was basically doing something like this:

parameter2 = getattr(someobj, 'parameter2', 'default')
# ... snip ...
self.addMySubobject('id', 'parameter1', parameter2, 'parameter3')

This code assumbed that parameter2 was a string attribute - but at some point the underlying code had changed for a subset of possible objects: On those "parameter2" was a method. You can add some object's method to another object in python, but you can't pickle the resulting object.

The solution was to add a simple check in the code:

parameter2 = getattr(someobj, 'parameter2', 'default')
if callable(parameter2):
    parameter2 = parameter2()
# ... and o on

Posted by betabug at 14:44 | Comments (0) | Trackbacks (0)
24 January 2008

That thing called #zope

A few thoughts on community or so
 

It seems that I hang out a lot on the IRC channel #zope on freenode.net. Today this was again noticed by a few people, when someone looking for help addressed me directly instead of the people on the channel in general. Usually I'm on #zope when I'm at work, and most of the time my work comes along pretty well to answering the occasional question. Despite helping with some problems, today I've committed something like 60-80 lines of code (it could have been more, but some problems require a lot of research and result in little code - in this case I was trying to find out why one of my pages crashes IE6). So it's not like I'm not doing anything besides "chatting" :-)

To me, #zope is a useful resource, even though there have only been a few questions I've asked (and got answers for) versus the many I'm helping with. But I remember some times when #zope got me out of deep water. Helping out on IRC really is an easy way to contribute to a community, and for a developer dependent on a particular Open Source software, the community has a big importance. In #zope I seem to have the role of asking back, asking for details and tracebacks, even when I don't know anything about the particular area in question. This often seems to have the result that someone more knowledgeable unidles and provides the real answer.

On the other hand I'm sending a lot of people to #plone, the channel for a certain CMS which I'm not particularly fond off, which is built using Zope. Many new users can't really distinguish, so they're either unaware of the appropriate channel, or they ask in the wrong channel just because they didn't receive a reply right away in the proper one. Plone has its own particular set of problems (probably a set from the BTree package, so all of them can fit in). The people who know these problems have their own channel, so it's really worth it asking in the right place. Also keep in mind that these are all volunteers helping out in their spare time: Show some patience!


Posted by betabug at 19:15 | Comments (3) | Trackbacks (0)
31 January 2008

Moblog Posting Through Procmail in COREBlog

Why wait for POP3?
 

The COREBlog blogging software for Zope has a nice "moblog" feature, which allows to post by email. Unfortunately it uses a POP3 account, and therefore has to be polled through a cronjob to check that POP3 account. That introduces a delay on mobile posts and results in a lot of unnecessary jobs being run on the server. Instead of this it's fairly easy to set up procmail to post through mail more directly...


This recipe involves three things:

Adding receive_from_msg() to COREblog.py

When thinking about this, at first I was carrying big plans about having to parse mails, check everything myself and splice together a method that adds a post. All of that wasn't really necessary, because the base of it is already in the current moblog implementation. All I had to do was to reuse 3 lines of code almost unchanged (and of course debug it for a while to find out which little part I had to change).

So, here is the patch:

Index: COREBlog.py
===================================================================
RCS file: /home/betabug/work/cvs/COREBlog/COREBlog.py,v
retrieving revision 1.11
retrieving revision 1.12
diff -u -r1.11 -r1.12
--- COREBlog.py 23 Oct 2007 13:12:40 -0000      1.11
+++ COREBlog.py 30 Jan 2008 15:14:35 -0000      1.12
@@ -1529,6 +1529,15 @@

         m.quit()

+    security.declareProtected(AddCOREBlogEntries,'receive_from_msg')
+    def receive_from_msg(self, msg):
+        """
+        Receive mails from a mail msg,
+        used e.g. to post messages throuh procmail.
+        """
+        buf = StringIO(msg)
+        mail = Message(buf,self.getProperty("management_page_charset"))
+        self.add_mail_entry(mail)

     security.declarePrivate('add_mail_entry')
     def add_mail_entry(self,mail):

All the other setup in COREBlog can be left as-is. We use the same accounts you would have set up for the normal moblog setup, along with settings / permissions / passwords. The method picks up a mail message in standard Unix mbox format (or however that's called), processes it and adds a post if everything is OK. As with standards moblog, you can add one image.

Mail server setup

I'm not going into this too much. My server is set up to allow users to process their mail through procmail, by adding a file called .procmailrc to their home directory. Obviously it also successfully receives mail through SMTP. The whole point of this recipe is to have your own server where you can play like this and don't need to go through POP3 accounts.

Mail account and procmail

The same account I had previously used for POP3 moblog mails now receives a .procmailrc with something like this inside:

DEFAULT=/var/mail/account_mailbox

:0 HB
* From:.*user@example\.org
* ^secretPassword123$
| /usr/local/bin/curl -u 'zopeaccount:secretZopePassword123' -F 'msg=<-' \
   http://example.org/path/to/receive_from_msg

The last 2 lines are actually one single line - I don't know if you really can wrap lines in a procmailrc, this is just for blog readability. So you might want to join them into one big line.

What this does:

It sets up a rule that searches header and body (HB), it processes only mails coming from a certain account, it checks for the moblog password (which in standard COREBlog moblog format appears on a line by itself) and then it pipes the resulting message into curl. In turn, curl posts the message to our method, using the form parameter msg and giving the username/password of a special Zope user account.

The checking of the password is performed by coreblog too, so it wouldn't be necessary here. But I like this check to be performed as early as possible, so Zope has less work to process any arbitrary mails which might arrive on this account. Keep in mind that the moblog password isn't really secure: It appears in plain text mails, so if you have high security standards, don't use this setup. In such a case you could use a script that checks a PGP signature and pipes into coreblog only when the signature matches.

The result

Now when I post to my blog using my mobile phone, the posts appear almost instantaneously, no need to wait for the cron job to come around. All the other setup remains the same.

See also: CoreBlogChanges

Posted by betabug at 09:33 | Comments (0) | Trackbacks (0)
16 April 2008

Missed Episodes: ZopeEditManager

Nice for ZWiki editing

The few times in many years that I tried out the ExternalEditor product I a.) never got it to work on Mac OS X and then b.) had already moved away from Through-The-Web (TTW) editing and therefore felt no more need to follow up on it. Today, following a problem someone had on #zwiki, I looked at ExternalEditor again, only to see that I had "missed episodes" of the show (as a Greek expression goes), since there is now a nice GUI application to handle the client side on Mac OS X: ZopeEditManager.

Since I'm not going to move back to editing Zope code in the ZMI, instead preferring to write my code on the file system, what could be the use for this thing for me now? Simple: ZWiki integrates very well with ExternalEditor, so having this thing installed, would allow me to edit wiki pages in a proper text editor. In fact, it works really well, took only a minute to configure for FireFox (after reading the Readme.html).

Next step would of course be to go back to a unixish setup, since my editor of choice is vi now... :-)


Posted by betabug at 15:23 | Comments (5) | Trackbacks (0)
03 May 2008

Like Riding a Build Cycle... You Never Forget

Picked up on Zwiki again a bit

After some months of above average slacking, I picked up on Zwiki hacking again a bit the last few days. I'm enjoying it, but it took some time to get started again. Where is the stuff again? How did we do this, that, the other thing? We also changed some things in the way we worked, with checkin messages now going to GeneralDiscussion (unfortunately they have to be cleaned up manually).

Speaking of such stuff, I really enjoyed this article called Software Builders by Diomidis Spinellis, quote:

"As much as I nostalgically remember the days when I could cook and eat dinner while compiling an application, a quick build cycle can keep developers focused..."

Wonderful! Back to Zwiki, we're (actually mostly Simon is - as mentioned I've been slacking a lot) moving the code base to Unicode for the data storage. Even though it's complicated, I've got a good feeling about it. We also have a Roadmap 2008 now. We've got an -unstable branch, which currently is at the point where it powers zwiki.org without much trouble. At first the Roadmap said that Plone support is to be terminated, but lately it's back, on minimal burner (no, I don't care, as long as I don't have to look at that stuff).

What I really would like next, is to test for unicode/utf8 transitions in our functional tests (built upon zope.testbrowser), but so far I haven't found out how I could do that. I'm talking about going through a cycle of page creation / edit / display page / search page with mixed Greek and German text and properly checking the displayed content at each stage. Problem is, it's all in a text file and my first tries resulted in UnicodeErrors even for stuff that worked in a browser. Of course I'm doing something wrong there.


Posted by betabug at 14:42 | Comments (1) | Trackbacks (0)
26 May 2008

How to add a Zwiki programmatically

Up to your own choices

Zwiki of course has a method manage_addWiki to add a wiki from a script. But this will add the "basic" standard wiki to your Zope ZODB. We had a plan here where we would add a special wiki for each user account... wasn't done (yet), but out of it came a simple code snippet that shows how to do it...


This is a method in one of our "container" products. It's not part of the class, as the plan was to run it from manage_afterAdd, but of course you could change it a bit and run it from the class too (in which case you'd probably change "add_point" to "self").

def add_user_wiki(add_point):
    # we're using BTreeFolder2
    # could be that someone wants a real big wiki
    # but also it's showing another difference to manage_addWiki
    add_point.manage_addProduct['BTreeFolder2'].manage_addBTreeFolder(
        'notebook', 'My Notebook Wiki')
    nb = add_point.notebook
    # add a wiki page with minimal welcome text
    nb.manage_addProduct['ZWiki'].manage_addZWikiPage('Hello')
    # set properties
    nb.manage_addProperty('allowed_page_types',
        ['rst','html'],'lines')
    nb.manage_addProperty('default_page','Hello','string')
    # set permissions
    nb.manage_permission('Zwiki: Add pages',
        ('staff','account','user'), acquire=1)
    nb.manage_permission('Zwiki: Add comments',
        ('staff','account','user'), acquire=1)
    nb.manage_permission('Zwiki: Edit pages',
        ('staff','account','user'), acquire=1)
    nb.manage_permission('Zwiki: Delete pages',
        ('staff','account','user'), acquire=1)
    nb.manage_permission('Delete objects',
        ('staff','account','user'), acquire=1)
    nb.manage_permission('Copy or Move',
        ('staff','account','user'), acquire=1)
    # shortcut to create catalog, create index_html:
    nb.Hello.upgradeAll()
    hello_text = """Hello!
----------

This is your **notebook**, where you can leave notes,
instructions, thoughts,... anything for you and your
friends to work together!

You can edit the text of this page and add new pages
as you please."""
    nb.Hello.edit(text=hello_text, log='initial content', type='rst')

The result is a properly set up wiki, but with just this one page and the permissions set to allow actions for a certain set of roles only. It wouldn't make sense to have a method like this in the Zwiki code, as the code is mainly setup and choices of configuration - everybody would likely want to run something different here.

Posted by betabug at 11:26 | Comments (0) | Trackbacks (0)
07 June 2008

Patch Fun

Instant gratification
 

For a few days now I've been hacking with Wu on a fun project of his. It's nothing too serious or even too useful, but that's not the point. For him it's a project to learn a bit more about Zope 2, while for me... for me it's just fun to show him how to do stuff clean and in "the right way" in Zope. (Yes, there's a lot of room to do things clean in Zope 2.) We're talking about simple stuff here, I sometimes wrote a patch between waking up and having breakfast. It's also not too serious, and the small scale gives me instant gratification.

Now looking through the patches one after another, I notice that they're a bit like an online course for an aspiring Zope programmer. I try to explain well what I did in the check in comments, as my target isn't writing a lot of code, but teaching through writing code.


Posted by betabug at 22:35 | Comments (3) | Trackbacks (0)
Prev  1   2   3   4   5   [6]   7   8   Next