<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-3585841838339962909</id><updated>2011-11-15T07:57:41.030-08:00</updated><category term='plone'/><category term='POSKeyError'/><category term='errors'/><title type='text'>PloneChix</title><subtitle type='html'>Empowered by Plone</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://plonechix.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3585841838339962909/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://plonechix.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>eleddy</name><uri>http://www.blogger.com/profile/12829844320869028742</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_3RIl2f8RMJQ/R_ZvRpo1IJI/AAAAAAAAB3Y/8t5B8YVPTFc/S220/DSC00141.JPG.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>14</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-3585841838339962909.post-3611418057935274954</id><published>2011-08-24T12:39:00.000-07:00</published><updated>2011-08-24T12:41:24.171-07:00</updated><title type='text'>Talking about Talks</title><content type='html'>&lt;div&gt;When we started planning the conference I intended to blog about EVERYTHING as a form of documentation. Whoops. No time like the present to catch up!&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;Without a doubt, setting up talks has been the most time consuming process in the conference planning so far. I want to shed some light on what I did this year, why it took so long and why it was so painful. This will give me some much needed catharsis and I would also be&amp;nbsp;interested&amp;nbsp;in hearing your feedback in the comments or elsewhere. The goal is to provide a solid, pain free template for next years organizers and for anyone else planning a conference in the future.&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;The Plan&lt;/span&gt;&lt;/div&gt;&lt;div&gt;The plan was simple enough: setup a Google moderator account and get feedback on what people want to see. Everyone who is planning on submitting will vet or obtain ideas there. Then, have people submit the talk to the ploneconf.org website with their bio and their talk (which will be magically normalized of course). The netsight product will take care of voting, scheduling, and display. Clean hands, everything&amp;nbsp;automated, go to the pub.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;The Reality&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Google moderator worked "just alright". The main issue was that most people thought this was the talk submission process, and were pimping for votes SXSW style. This made it super confusing when we actually asked for talks to be submitted. Nonetheless, feedback was gathered and there were a lot of interesting ideas thrown around.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Talk Submissions&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Our "plan" did not include validating what was done last year code-wise ahead of time. We didn't realize that no permissions were set up for an open site, and everything was completely customized for Bristol. We never got logins integrated like we wanted either. Whoops. Deadline and pressure was mounting so we threw up a Google Form. Enter stage left: the vile mistress Regret.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Immediately we realized that people couldn't edit their submissions after clicking submit and started emailing us changes instead. Also, people had to triplicate their bios and many of them said things like "see other bio". This was not really fair to the submitters or us. At least the form looked nice, was embed-able, and I could add fields as people reminded me that I was missing important things like email address and first time speaker status (thanks to all those who took the time to send feedback - this is all now documented for next year). Totally not worth it though.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Deadlines&lt;/span&gt;&lt;/div&gt;&lt;div&gt;We decided up front there would be no extensions so that lit a fire under us to make sure people submitted&amp;nbsp;(I personally get annoyed by our "deadline&amp;nbsp;extended!"&amp;nbsp;community habit).&amp;nbsp;The post for talks went up and they start coming in quickly. At 3.5 days and 2 rooms, we were looking at around 65 talk slots to fill (not including related technologies track which was organized a bit differently). There was a big bump in submissions at first but then things started to trickle down to the point where we were nervous wouldn't reach our target number of talks.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;So we set up a raffle for a free conference ticket and doubled newbie chances to win. This went REALLY well, and encouraged people to submit multiple talks. Later, this gave me the opportunity to pick the strongest talks for a particular person and not have to turn down a great speaker that just picked a bad topic. This gave us plenty of great talks to chose from.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The other thing that happened is that people didn't believe the deadline was real and/or missed it. Some people submitted by email. Others by Google docs. Some talks were "implied". This process went on far too long. To give you an idea what I was dealing with, here are talks to format stats:&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;Google Forms: 72 (4 were submitted late)&lt;/li&gt;&lt;li&gt;Google Docs: 17&lt;/li&gt;&lt;li&gt;Email: 4 (2 never responded with bios)&lt;/li&gt;&lt;li&gt;IRC: 1&lt;/li&gt;&lt;li&gt;Chat: 1&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;If I messed up your talk, I deeply apologize but hopefully you understand why things slipped through the cracks. This goes back to the earlier point on not being able to edit talks. Lesson learned.&amp;nbsp;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Voting&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Once all the talks were in we had to import them into the conference website for voting (for those keeping count, this is the 3rd e-format for talks). It took a good 2/3 of a day to set up the styles, update the CSV importer and Dexterity types, and deploy. It was easy drudgery. At some point a unicode problem was introduced we couldn't have any authors or titles with non-ascii letters. By that point I said "f*ck it", ran the import, and asked for help in manually cleaning up the bad encoding. Many thanks to bdbaddog on that one. This could all be avoided in the future.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Voting again was somewhat confusing since "we already voted". The controls aren't super intuitive but luckily they were the same as last year and didn't require authentication. All the reports were already there that I needed and people seemed to figure it out just fine. I will mention that there wasn't a single surprise result in there. Everything lined up just nearly exactly as I thought it would. It did give me numbers to work with for scheduling, which was a huge benefit (technique will be described below).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Scheduling&lt;/span&gt;&lt;/div&gt;&lt;div&gt;After all the votes had been cast, I stared at the computer for at least 2 hours thinking about where to go next. No matter what, code had to be updated (last years schedule was a hard coded html template) and it would be nice to make something reusable but I wasn't sure if it was possible in a timely manner. I looked at a couple calendar integration tools but none of them played well with the Dexterity type we were using. In the end I resorted to &lt;a href="http://twitpic.com/6a5qm7"&gt;notecards and giant blocks of paper.&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Process pseudocode (maybe someone can code this up for next year):&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;ol&gt;&lt;li&gt;Transfer talk title, speaker, and vote count to postcards (format #4). If the talk was 45 minutes, leave it fill size. If it was 30 minutes, cut it in half. Talks go on 1 of 4 colored postcards: All, Beginner, Intermediate, or Advanced. This refers to level of development experience needed to see the talk. This was the easiest way for me to think of preventing talk conflicts. Note: I read what the speaker claimed their audience was but 9 times out of 10 there was a&amp;nbsp;discrepancy&amp;nbsp;from what was claimed and what the extended talk summary said. In those cases, I went with my gut.&lt;/li&gt;&lt;li&gt;Sort each pile by number of votes. For each pile with too many talks, move the worst rated &lt;b&gt;n&lt;/b&gt; talks to a&amp;nbsp;separate&amp;nbsp;pile. They aren't excluded, just removed from consideration until all the most popular talks are handled. Try to make all the piles an equalish size. Set aside 1/3 of the worse rated talks for multiple submitters for the same reason in a different pile.&amp;nbsp;Talks that are almost exact duplicates get the same treatment. Side note: the&amp;nbsp;person who submitted the most talks was Carlos de la Guradia at a whopping 6 talks!&lt;/li&gt;&lt;li&gt;Draw a big calendar. I used some GIANT post it notes. Lay out the tracks and physically write in things that can't be changed such as keynotes, lightening talks, foundation meetings. Set up a plan ahead of time. I decided I would put "All" and "Beginner" talks in track 1, "Intermediate" and "Advanced" talks in track 2, and "Related Technology" talks in track 3. For each track I alternated between all and beginning, intermediate and advanced as much as possible. I pitted "All" talks against "Intermediate", and "Beginner" against "Advanced" as much as possible in attempt to avoid beginner/intermediate talk clashes.&amp;nbsp;&lt;/li&gt;&lt;li&gt;Work in breaks and lunches. I went for a break or lunch about once every 2 hours.&lt;/li&gt;&lt;li&gt;After getting all the super high rated talks mapped out and un-conflicted, pull in the reserve stacks and added them in by popularity and amount of space left in the track. Flex time talks really came in handy. The size of the cards fit perfectly for my mocked up calendar.&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;&lt;div&gt;With this strategy, the hardest scheduling was the related technology track, since there would inevitably be clashes between the more advanced talks and those. When I had a conundrum, there was almost always a similar or duplicate talk in my backpocket that I just threw into the schedule at a different time. It took me a moment to get over the fact that there is no harm in having almost the same talk twice if they are both&amp;nbsp;highly&amp;nbsp;rated and well respected speakers: a conference schedule is not an ER diagram!&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Last but not the very least was putting these talks in electronic form. I gave up on using the conference talks add-on as soon as I found SCHED.org. After an hour of beer and wallowing over the fact that I should have used that from the beginning, I spent the next 8 hours adding speakers, talks, keywords, etc to the new site (format #5). There is an API but all of the talks needed to be keyworded, slotted, and mapped to users so I sucked it up and just hammered it in. So much for automated.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;And the 2011 &lt;a href="http://2011ploneconference.sched.org/"&gt;Plone Conference Schedule&lt;/a&gt; was born.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Hindsight&lt;/span&gt;&lt;/div&gt;&lt;div&gt;No one is paying me hourly to make decisions for this conference so I have a&amp;nbsp;tendency&amp;nbsp;to revert to geeky habits and "make" or "wrangle" things instead of searching the web for tools and starting there. At first I was fitting conference stuff in at the end of the day and making poor choices. Now I tend to block of entire "conference days" to help reduce the decision fatigue factor. It helps a lot.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;There is also a natural&amp;nbsp;tendency with the conference to just get things done so people "stop bugging me about it" or because "if we don't do this soon we are screwed". I can't speak for past organizers but I have a feeling it's one of the core reasons why there is no reusable conference framework despite all the years of the conference being around. Day job money always trumps conference "&lt;painful_joke&gt;profit" [&amp;lt;-- hilarious!]&lt;/painful_joke&gt;. It really takes a solid effort and motivation to do the right thing for the future. Just writing this post helps keep me focused and remembering to reuse where possible, be it our own code or someone&amp;nbsp;else's.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Suggestions for Next Year&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;Get feedback on whether or not moderator actually influenced talks this year before going that route again. I have a sneaking suspicion it didn't make that much difference. Instead, use feedback from THIS years conference to purposely direct the speakers for next year. You could also encourage people to submit multiple talks from the beginning and weed out the weakest ones later.&lt;/li&gt;&lt;li&gt;Beware of the seductive siren that is Google Forms.&amp;nbsp;&lt;/li&gt;&lt;li&gt;Do the raffle for a free speaker ticket again. In fact, give away several if you can manage.&lt;/li&gt;&lt;li&gt;As a potential conference speaker, please please please use whatever form was submitted and submit your talk on time.&amp;nbsp;I highly encourage next years organizers to set and enforce rules on talk submissions. "Go here and do it by X or it's out". The favors and special considerations will kill you. They killed me, and I didn't even honor all of them I think. I don't even know if I did or not!&lt;/li&gt;&lt;li&gt;Use sched.org or something like it from the get go. Plone is not good for this. SCHED prints out nicely and can be print out and cut up for manual organizing. It is mobile ready. It takes talk&amp;nbsp;submissions&amp;nbsp;and has a voting process. It is everything. I paid a $99 fee to get an add free version. Totally worth it. If someone steps up to sponsor the mobile version, we will be even better off.&lt;/li&gt;&lt;li&gt;There are still some things missing with SCHED. Do your research before jumping on it again. I have already started bothering them for key missing features so maybe next year they will have their shizzle together more. In your research, don't forget things like: voting, editing your own profile and talk, multiple tracks and venues, mobile support. Bonus if you find ajaxy drag and drop and auto buffers for talks.&lt;/li&gt;&lt;li&gt;As far as scheduling, the alternate talk sizes was a slight bit more overhead but in the end it actually gave me a bit of play room and the ability to fill empty slots. I will be interested to see how that goes.&lt;/li&gt;&lt;li&gt;We set up a ton of mailing lists for speakers, sponsors, etc with mailchimp. At the end of the conference, don't let 2011 organizers forget to get feedback for 2012. We tried a lot of new things like variable talks,&amp;nbsp;staggered&amp;nbsp;schedules, related tech - all of them need to be evaluated and reconsidered for next year.&lt;/li&gt;&lt;li&gt;If you have a bunch of people working dev, make sure someone is NOT developing and can be a manager. We know this on contracts: don't forget to apply the same philosophy here. (If I say it enough, I won't let it happen again).&amp;nbsp;The perfect example is when we started "integrating" Brown Paper Tickets into the site. Put a bunch of people super geeked to give back into one room and all the sudden you've integrated something that doesn't need integrated to begin with (thanks to Jon Stahl for bringing some reason to that situation before it got out of hand). I should not have been even trying to dev anything in this scenario - role mixing is like vodka and redbull: it feels awesome at the time but the aftermath often involves vomit.&amp;nbsp;&lt;/li&gt;&lt;li&gt;While Brown Paper Tickets is local and a bit cheaper, Event Brite is integrated into just about everything already. Mailing list tools, scheduling tools, etc all support importing users directly with the API. Something to consider.&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;FAQ&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;&lt;b&gt;Shouldn't "The State of Plone" talk be a keynote? &lt;/b&gt;We have a unique situation with space this year in that none of the rooms can &lt;i&gt;technically&lt;/i&gt; hold more than 300 people (fire marshall Liz says they can). For the main keynote we rented out a space in the movie theater that is attached to SFSU. It will be awesome: you can drag your hungover butt in, eat popcorn and nachos, and get your mind melted. But it's $1000/hour. Yeah. Time to get in the movie theater business.&amp;nbsp;&lt;/li&gt;&lt;li&gt;&lt;b&gt;Does that mean multiple tracks for Lightening Tracks and popular talks?&lt;/b&gt; No. We will be&amp;nbsp;broadcasting&amp;nbsp;live to overflow rooms in these cases. The crowd tends to thin out for these so hopefully we won't need them. We will use the preliminary results from scheduling on sched.org to decide which talks get overflow rooms in general. In those cases, we ask that people who are not actually paying attention use the overflow. You know who you are Mr/s. Email McYoutube.&lt;/li&gt;&lt;li&gt;&lt;b&gt;My bio is weird - I didn't write one - what happened?&lt;/b&gt;&amp;nbsp;If you didn't write your bio, there is a good chance that I did (under the influence of beer and exhaustion of course). Feel free to log in and change it.&amp;nbsp;&lt;/li&gt;&lt;li&gt;&lt;b&gt;Can you fix talk X clashing with talk Y and move talk Z? &lt;/b&gt;Sure. If you give me the full swap formula and it passes through my "we can't do that because XYZ drama" filter at talks@ploneconf.org I'll seriously consider it. &lt;chuckle&gt; Also, please note that these will all be recorded so you can go back and watch something at any point in the future.&lt;/chuckle&gt;&lt;/li&gt;&lt;li&gt;&lt;b&gt;Will this schedule be changing?&lt;/b&gt; Likely. The general timeline is not going to change and key events will not move. Some talks may get shuffled here and there though and I fully expect some last minute additions/subtractions.&lt;/li&gt;&lt;li&gt;&lt;b&gt;Wait, did you say reusing from last year? &lt;/b&gt;Yep. All the work we do on the site is open and started with an injection from the 2010 Plone Conference site code by Netsight. I encourage anyone who wants to change something to contribute at&amp;nbsp;https://github.com/Plone-Conference-Devs. Little by little we can make a difference for the future. Hopefully in 5 years, everything will be easy peasy. Well, the tech part anyways. The dealing with people part needs its own documentation.&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;Many thanks to all the feedback, advice, and help along the way. &amp;nbsp;Feel free to comment below on changes, suggestions, etc.&amp;nbsp;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3585841838339962909-3611418057935274954?l=plonechix.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plonechix.blogspot.com/feeds/3611418057935274954/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plonechix.blogspot.com/2011/08/talking-about-talks.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3585841838339962909/posts/default/3611418057935274954'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3585841838339962909/posts/default/3611418057935274954'/><link rel='alternate' type='text/html' href='http://plonechix.blogspot.com/2011/08/talking-about-talks.html' title='Talking about Talks'/><author><name>eleddy</name><uri>http://www.blogger.com/profile/12829844320869028742</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_3RIl2f8RMJQ/R_ZvRpo1IJI/AAAAAAAAB3Y/8t5B8YVPTFc/S220/DSC00141.JPG.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3585841838339962909.post-7340671638387800246</id><published>2011-08-08T20:15:00.000-07:00</published><updated>2011-08-09T10:36:17.349-07:00</updated><title type='text'>10 Minute Caching with Apache</title><content type='html'>The problem with caching is that it encompasses 3 layers of the stack, and requires a good solid read to understand what's really happening. You should enable browser caching on every site with more than 10 users, and proxy caching for resources pretty soon thereafter. This is my attempt to distill this down into a method that will get you "far enough".&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Goal&lt;/b&gt;: cache everything, except that which is content in the browser &lt;i&gt;and&lt;/i&gt; in a disk cache.&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;b&gt;Install and activate plone.app.caching (aka HTTP Caching)&lt;/b&gt;. This is included by default after Plone 4.1. Use it. On the first tab: enable gzip and enable caching. Leave caching proxies and in memory cache at the default. On the third tab: Content files and images get moderate, content folder and content items get weak, and file and image and stable file and image get strong. You may want unstable file and image to be moderate but... I doubt it. When in doubt, use less caching. Last but not least, in the last tab, weak caching is configured to use last modified (that's it), moderate caching is default with just max age set, strong caching is same as moderate but up the expiration time to 864,000, and also check store in RAM cache. This number means those resources will be 10 days to be in the proxy/browser cache, and you can probably up that significantly as your caching chops get sharper. You may be tempted to put &lt;i&gt;everything&lt;/i&gt; in ram cache at this point in time, but don't. That's for people who like to debug caches.&lt;/li&gt;&lt;li&gt;&lt;b&gt;Install Apache 2 and Configure Your Site&lt;/b&gt;. You only need to do this if this is a new site and/or it's not already configured. Many linux installs actually have this by default. &amp;nbsp;Some good ubuntu instructions are &lt;a href="https://help.ubuntu.com/8.04/serverguide/C/httpd.html"&gt;here&lt;/a&gt;. You probably want to enable rewrite and proxy as well, in which case you will need to a2enmod rewrite, proxy, proxy_http as well if you want to do rewriting, but this post isn't about that :)&lt;/li&gt;&lt;li&gt;&lt;b&gt;Configure Apache as a disk cache&lt;/b&gt;.&amp;nbsp;Basic instructions are &lt;a href="http://pashabd.blogspot.com/2011/06/apache-modcache-ubuntu-10.html"&gt;here&lt;/a&gt;. You will need to add one key line: "CacheIgnoreNoLastMod On". This is because the js and css resources don't have a modification time by default and we still want them cached on disk. In fact, these are the resources that we care the MOST about being cached. Make sure to set up the cache cleaning process. Check out the &lt;a href="https://gist.github.com/1133260"&gt;example site config&lt;/a&gt; to see how simple it can be. Note that this is just a balls to the wall quick write up. All apache is doing here is looking at your caching headers from p.a.caching and doing the same thing that the browser would. When configured properly, after the first fetch, Plone will never render any css, js, or image files again.&lt;/li&gt;&lt;li&gt;&lt;b&gt;Validate&lt;/b&gt;. Use LiveHeaders in Firefox or look at the network diagrams in Chrome/Safari. JS, CSS, and [non-content] images should never 304. They should download on the first pass and then always pull from cache. Pages, folders, or any other content should always 200. If you see any 404s in the meanwhile, stop immediately and fix them. Clear the cache. Validate that all new page requests come from the cache.&lt;/li&gt;&lt;/ol&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;FAQ&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;ol&gt;&lt;li&gt;&lt;b&gt;How do I know its hitting the cache? &lt;/b&gt;I'm so glad I pretended like you asked. There is no built in way to do this but with some clever jiggering it's easy. From the command line, tail the Z2.log(s) for each running zope instance. (Pst: you can tail multiple logs at once by just listing them with a space in between). Then, open up the browser. Load a page. Notice that everything is pulled the first time. Clear the cache. Load the page again. Note that all the resources have 200 OK status (meaning they were actually retrieved from the servers) but there is no entry in your Z2.log. It's a festivus miracle! This means that they were served from apache.&lt;/li&gt;&lt;li&gt;&lt;b&gt;What about HAProxy? &lt;/b&gt;That's nice, and I totally recommend it. But not required.&amp;nbsp;&lt;/li&gt;&lt;li&gt;&lt;b&gt;Should I use an apache in buildout? &lt;/b&gt;No. Unless you want to hate yourself or you are an expert sysadmin [that is trying to get fired]. Tutorials don't care about buildout and you want to have access to tutorials.&lt;/li&gt;&lt;li&gt;&lt;b&gt;Shouldn't I be using varnish too?&lt;/b&gt; Perhaps. I personally have never liked it, and there is no one config out of the box that makes me happy. All of my sites are closed, pretty dynamic and include 3rd party integration so caching individual pages never suits me. Remember, this is a 10 minute way to get basic results. Varnish is really overkill for a lot of sites and will just give you more headache than its worth.&amp;nbsp;&lt;/li&gt;&lt;li&gt;&lt;b&gt;Purging?&lt;/b&gt; That's for another tutorial.&amp;nbsp;&lt;/li&gt;&lt;li&gt;&lt;b&gt;What exactly is cached here and where?&lt;/b&gt;&amp;nbsp;This does NOT cache and views or pages. That's the point. It's simple enough for debugging and small setups while still redirecting "crap traffic" to another server. It caches just enough and no more. I'm pretty sure there is a your mom joke in here somewhere...&lt;/li&gt;&lt;li&gt;&lt;b&gt;Isn't apache for old people?&lt;/b&gt;&amp;nbsp;Beat it punk.&lt;/li&gt;&lt;li&gt;&lt;b&gt;Did you test these instructions?&lt;/b&gt; No, they were written in anger and rush. Comment if I messed up and I'll help you then update this. If there is significant interest I can do a more detailed post. This assumes a minimum sysadmin knowledge.&lt;/li&gt;&lt;li&gt;&lt;b&gt;What if I want to force fresh css/js?&lt;/b&gt; Easy. Just reinstall your product &lt;i&gt;OR&lt;/i&gt; turn development mode on then off for js and/or css. This causes a new file name to be generated and the caching cycle to start over again. For static images, you may be in a harder situation. I have just relegated to create a new name for the images each time. I find that these rarely change since these images are almost always going to be from design. If you can wait the 10 days (or whatever time you set for strong caching) you don't need to do anything. \o/&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3585841838339962909-7340671638387800246?l=plonechix.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plonechix.blogspot.com/feeds/7340671638387800246/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plonechix.blogspot.com/2011/08/10-minute-caching-with-apache.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3585841838339962909/posts/default/7340671638387800246'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3585841838339962909/posts/default/7340671638387800246'/><link rel='alternate' type='text/html' href='http://plonechix.blogspot.com/2011/08/10-minute-caching-with-apache.html' title='10 Minute Caching with Apache'/><author><name>eleddy</name><uri>http://www.blogger.com/profile/12829844320869028742</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_3RIl2f8RMJQ/R_ZvRpo1IJI/AAAAAAAAB3Y/8t5B8YVPTFc/S220/DSC00141.JPG.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3585841838339962909.post-3529124959215714355</id><published>2011-06-09T19:10:00.000-07:00</published><updated>2011-06-09T19:10:11.900-07:00</updated><title type='text'>Programmatically Adding Image Content in Generic Setup</title><content type='html'>Have you been sitting around for the last hour thinking "man, it would sure be great if I could add Images to my site on install but I just can't find a code example"? I know I was. Consider it documented!&lt;br /&gt;&lt;br /&gt;There will be a slideshow in the Plone Conference 2011 website (it's coming soon I swear!) that we wanted to work on install since it was 1. on the front page and 2. a PITA to set up every time. The code below gives "a" way to do this, although it could be taken to a much more abstracted level (what couldn't?!?).&lt;br /&gt;&lt;br /&gt;This assumes that there is a folder called images in your generic setup profile folder, which is likely to be in ... &amp;gt; profiles &amp;gt; default &amp;gt; images. The nice thing is that it doesn't rely on the magical, mystical .content jiggery. I won't rehash the code comments, but if you are using the &lt;a href="http://plone.org/products/collective.easyslideshow"&gt;slideshow product&lt;/a&gt; then maybe this cantation is for you!&lt;br /&gt;&lt;br /&gt;&lt;script src="https://gist.github.com/1018109.js?file=fart.py"&gt;&lt;/script&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3585841838339962909-3529124959215714355?l=plonechix.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plonechix.blogspot.com/feeds/3529124959215714355/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plonechix.blogspot.com/2011/06/programmatically-adding-image-content.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3585841838339962909/posts/default/3529124959215714355'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3585841838339962909/posts/default/3529124959215714355'/><link rel='alternate' type='text/html' href='http://plonechix.blogspot.com/2011/06/programmatically-adding-image-content.html' title='Programmatically Adding Image Content in Generic Setup'/><author><name>eleddy</name><uri>http://www.blogger.com/profile/12829844320869028742</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_3RIl2f8RMJQ/R_ZvRpo1IJI/AAAAAAAAB3Y/8t5B8YVPTFc/S220/DSC00141.JPG.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3585841838339962909.post-748167773682891014</id><published>2010-11-17T15:42:00.000-08:00</published><updated>2010-11-17T15:42:04.341-08:00</updated><title type='text'>Plone, JQuery and Prototype: Easier Done Than Said</title><content type='html'>It's hard to believe, but jQuery hasn't always existed. So some of us used other libraries, like Prototype[.js]. We even built entire systems on them. And then upgrade day came...&lt;br /&gt;&lt;br /&gt;I sat back with my largest bottle of beer and got ready for what was sure to be a javascript&amp;nbsp;apocalypse. How could I merge 4 years of Prototype based javascript with Plones new JQuery libs? Surely it would be painful.&lt;br /&gt;&lt;br /&gt;And yet it wasn't. It will take me longer to write this blog post. But just in case there is some other&amp;nbsp;straggling&amp;nbsp;soul like myself who just can't get enough Event.observe(), fear not! Follow these simple steps:&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Create a new js file and add one line:&amp;nbsp;jQuery.noConflict();&lt;/li&gt;&lt;li&gt;Then register that js file with your site. Prototype must go first or it will whine (remember not to compress or cook!). The noConflict js file must be listed before using any custom scripts. I recommend loading it right after jQuery.js. See below for a sample.&lt;/li&gt;&lt;li&gt;Reinstall.&amp;nbsp;&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;Much can be said about jQuery playing nice and especially to the Plone javascript gurus who were kind enough to work within a namespace. Thank you. I owe you a beer.&lt;br /&gt;&lt;br /&gt;&lt;script src="https://gist.github.com/704345.js?file=jsregistry.xml"&gt;&lt;/script&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3585841838339962909-748167773682891014?l=plonechix.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plonechix.blogspot.com/feeds/748167773682891014/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plonechix.blogspot.com/2010/11/plone-jquery-and-prototype-easier-done.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3585841838339962909/posts/default/748167773682891014'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3585841838339962909/posts/default/748167773682891014'/><link rel='alternate' type='text/html' href='http://plonechix.blogspot.com/2010/11/plone-jquery-and-prototype-easier-done.html' title='Plone, JQuery and Prototype: Easier Done Than Said'/><author><name>eleddy</name><uri>http://www.blogger.com/profile/12829844320869028742</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_3RIl2f8RMJQ/R_ZvRpo1IJI/AAAAAAAAB3Y/8t5B8YVPTFc/S220/DSC00141.JPG.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3585841838339962909.post-2624640982009207030</id><published>2010-09-14T17:24:00.000-07:00</published><updated>2010-09-14T17:30:25.663-07:00</updated><title type='text'>LA Theme Sprint Report</title><content type='html'>&lt;div class="" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em; text-align: left;"&gt;&lt;a href="http://2.bp.blogspot.com/_3RIl2f8RMJQ/TJAQ5J0ffHI/AAAAAAAAIbE/ToRYz0VEWwM/s1600/Sparkling-xdv-theme.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="200" src="http://2.bp.blogspot.com/_3RIl2f8RMJQ/TJAQ5J0ffHI/AAAAAAAAIbE/ToRYz0VEWwM/s200/Sparkling-xdv-theme.png" width="148" /&gt;&lt;/a&gt;Kudos to Michael Miller and Luke Brannon of the &lt;a href="http://laplone.org/"&gt;LA Plone&lt;/a&gt; Meetup for hosting the first (annual?) Plone Theming Sprint in LA. So many things about it were&amp;nbsp;successful&amp;nbsp;that I have had major writers block on how to get things started so I'm just going to jump write in.&lt;br /&gt;&lt;br /&gt;The most exciting part of the sprint was not only the diversity of the people, but the diversity of experience as well. Plenty of newbies overcame their fears to tackle new skills. For example, Heather released her very &lt;a href="http://pypi.python.org/pypi/collective.fsdsimplifier/1.0"&gt;first product to pypi&lt;/a&gt;, almost the whole crew picked up and ran with XDV,&amp;nbsp;and Alice was an end user who just showed up and by the end of the first day was familiar with installing products through buildout, basic site admin, and fixing borked databases!&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_3RIl2f8RMJQ/TJARUaB5sNI/AAAAAAAAIbM/AatPR2gVHh8/s1600/Welcome+to+Plone+%E2%80%94+Site.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" height="200" src="http://1.bp.blogspot.com/_3RIl2f8RMJQ/TJARUaB5sNI/AAAAAAAAIbM/AatPR2gVHh8/s200/Welcome+to+Plone+%E2%80%94+Site.png" width="132" /&gt;&lt;/a&gt;&lt;/div&gt;Why would her database get borked you ask? Because she went through and installed all 52 themes listed on plone.org to evaluate quality, take screenshots where necessary, and took copious notes of all posted themes that didn't uninstall cleanly or didn't install at all.&lt;br /&gt;&lt;br /&gt;Now you are asking, why would someone do that? Only because we tackled the revamping PloneSoftwareCenter! This included adding ratings and we needed some data for when it goes live. Oh yeah. Alec took &lt;a href="http://pypi.python.org/pypi/plone.contentratings/1.0-rc1"&gt;plone.contentratings&lt;/a&gt; from bad ass to bad asser while I worked on integrating and revamping the UI. Check out the &lt;a href="http://www.screencast.com/users/eleddy/folders/Jing/media/98b33d12-45b9-4d89-8c2a-4024fe60a21f"&gt;screencast&lt;/a&gt;&amp;nbsp;to get a preview of the changes. This will be a great&amp;nbsp;accompaniment&amp;nbsp;to the Plone 4 release so if you have any time at all to help get this deployed - ping me!&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_3RIl2f8RMJQ/TJASe16vJEI/AAAAAAAAIbU/b8kH8Ci0SY0/s1600/plone-indication.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="200" src="http://1.bp.blogspot.com/_3RIl2f8RMJQ/TJASe16vJEI/AAAAAAAAIbU/b8kH8Ci0SY0/s200/plone-indication.jpg" width="108" /&gt;&lt;/a&gt;&lt;/div&gt;And finally you ask, why would you want to revamp PSC? Glad you asked. Plone.org has less than spectacular support for navigating themes and we needed to have a beautiful place to display all of the new themes that the rest of the crew came up with. 3 Mikes, Trish, Tyler, and Albert banged away on creating 3 beautiful XDV themes (coming your way soon after solving the packaging issue).&lt;br /&gt;&lt;br /&gt;The best part of all? At the end all anyone wanted to know was when the next sprint would be. That's a trend I'm happy to obligue. Thanks to everyone who showed up and good work everyone!&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_3RIl2f8RMJQ/TJAFSZzzoUI/AAAAAAAAIa8/HJ_UGQuSVWk/s1600/2010-08-28_20-09-39_901.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="225" src="http://3.bp.blogspot.com/_3RIl2f8RMJQ/TJAFSZzzoUI/AAAAAAAAIa8/HJ_UGQuSVWk/s400/2010-08-28_20-09-39_901.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3585841838339962909-2624640982009207030?l=plonechix.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plonechix.blogspot.com/feeds/2624640982009207030/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plonechix.blogspot.com/2010/09/la-theme-sprint-report.html#comment-form' title='11 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3585841838339962909/posts/default/2624640982009207030'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3585841838339962909/posts/default/2624640982009207030'/><link rel='alternate' type='text/html' href='http://plonechix.blogspot.com/2010/09/la-theme-sprint-report.html' title='LA Theme Sprint Report'/><author><name>eleddy</name><uri>http://www.blogger.com/profile/12829844320869028742</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_3RIl2f8RMJQ/R_ZvRpo1IJI/AAAAAAAAB3Y/8t5B8YVPTFc/S220/DSC00141.JPG.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_3RIl2f8RMJQ/TJAQ5J0ffHI/AAAAAAAAIbE/ToRYz0VEWwM/s72-c/Sparkling-xdv-theme.png' height='72' width='72'/><thr:total>11</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3585841838339962909.post-6906198840095956259</id><published>2010-09-04T14:35:00.000-07:00</published><updated>2010-09-04T14:35:50.601-07:00</updated><title type='text'>Feed a QA Sprinter</title><content type='html'>The 2010 Plone Conference is just around the corner and sprint planning is well under way. One of the sprints will consist of creating Selenium tests and &lt;a href="http://sourceforge.net/mailarchive/message.php?msg_name=5A77FD09-4C29-4826-9CEF-072B77B79B3E%40psu.edu"&gt;integrating with Hudson&lt;/a&gt;.&amp;nbsp;As Plone moves towards more and more Javascript and Ajax interface these tests will be essential for maintaining a high quality code base.&amp;nbsp;This sprint will be great for newbies and veterans alike with some potentially boring but&amp;nbsp;definitely&amp;nbsp;worthwhile QA work.&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Ideally this sprint is crowded and short so that people can get back to their regularly scheduled sprints. If you will be in Bristol for the sprints, please consider donating 1-2 hours of time to help us get up and running. If you won't be in Bristol and want to remote sprint stay tuned! However, if you can't do either (or just hate doing that stuff) but want to support the effort, please consider chipping in to &lt;a href="http://ploneconf2010qasprint.chipin.com/plone-qa-sprint"&gt;feed a sprinter&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;If anyone has other ideas on how to lure people in or has items to donate as door prizes/giveaways/whatever I'm all ears. Additionally, if you have a company and/or want to sponsor this sprint directly let me know and I'll cancel the chip in.&lt;br /&gt;&lt;br /&gt;Happy bug hunting!&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;object height="250" width="250"&gt;&lt;param name="movie" value="http://widget.chipin.com/widget/id/8f32c4bf4b73fa7d"&gt;&lt;/param&gt;&lt;param name="allowScriptAccess" value="always"&gt;&lt;/param&gt;&lt;param name="wmode" value="transparent"&gt;&lt;/param&gt;&lt;param name="event_title" value="Feed%20a%20QA%20Sprinter"&gt;&lt;/param&gt;&lt;param name="event_desc" value="Plone%20Conference%202010%20at%20Bristol"&gt;&lt;/param&gt;&lt;param name="color_scheme" value="blue"&gt;&lt;/param&gt;&lt;embed src="http://widget.chipin.com/widget/id/8f32c4bf4b73fa7d" flashVars="event_title=Feed%20a%20QA%20Sprinter&amp;event_desc=Plone%20Conference%202010%20at%20Bristol&amp;color_scheme=blue" type="application/x-shockwave-flash" allowScriptAccess="always" wmode="transparent" width="250" height="250"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3585841838339962909-6906198840095956259?l=plonechix.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plonechix.blogspot.com/feeds/6906198840095956259/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plonechix.blogspot.com/2010/09/feed-qa-sprinter.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3585841838339962909/posts/default/6906198840095956259'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3585841838339962909/posts/default/6906198840095956259'/><link rel='alternate' type='text/html' href='http://plonechix.blogspot.com/2010/09/feed-qa-sprinter.html' title='Feed a QA Sprinter'/><author><name>eleddy</name><uri>http://www.blogger.com/profile/12829844320869028742</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_3RIl2f8RMJQ/R_ZvRpo1IJI/AAAAAAAAB3Y/8t5B8YVPTFc/S220/DSC00141.JPG.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3585841838339962909.post-5022852160805944868</id><published>2010-07-21T13:29:00.000-07:00</published><updated>2010-07-21T13:29:59.970-07:00</updated><title type='text'>Changing the Order of Fields in Archetypes</title><content type='html'>I wish I knew this 4 years ago.&lt;br /&gt;&lt;br /&gt;When creating new content based on archetypes, I usually start by copying the base schema, or one of my own base schemas with something like&amp;nbsp;MyNewSchema = BaseSchema.copy(). One thing that has been historically hard in my case is that the schema order in the code directly correlates to the order in which its displayed. This means that the base copied data always goes first when displayed, which was not always what I wanted. The solution was custom templates and blech all over.&lt;br /&gt;&lt;br /&gt;Apparently you can just reorder these things. Deep in the bowels of archetypes there is a function called moveField. Now, reordering the schema can me as easy as&amp;nbsp;MyNewSchema.moveField('description', pos='bottom'). &amp;nbsp;Hot dog! Pasting the interface for reference.&lt;br /&gt;&lt;br /&gt;&lt;script src="http://gist.github.com/485067.js"&gt; &lt;/script&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3585841838339962909-5022852160805944868?l=plonechix.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plonechix.blogspot.com/feeds/5022852160805944868/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plonechix.blogspot.com/2010/07/changing-order-of-fields-in-archetypes.html#comment-form' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3585841838339962909/posts/default/5022852160805944868'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3585841838339962909/posts/default/5022852160805944868'/><link rel='alternate' type='text/html' href='http://plonechix.blogspot.com/2010/07/changing-order-of-fields-in-archetypes.html' title='Changing the Order of Fields in Archetypes'/><author><name>eleddy</name><uri>http://www.blogger.com/profile/12829844320869028742</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_3RIl2f8RMJQ/R_ZvRpo1IJI/AAAAAAAAB3Y/8t5B8YVPTFc/S220/DSC00141.JPG.jpg'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3585841838339962909.post-4343527001361841961</id><published>2010-07-16T13:05:00.000-07:00</published><updated>2010-07-16T14:33:58.907-07:00</updated><title type='text'>The Culture of Reporting Bugs</title><content type='html'>&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;"&gt;Did you know that Plone has a QA team? It does! The &lt;a href="http://plone-qa.tool.net/"&gt;QA team&lt;/a&gt; is just getting off the ground, so it's a great time to talk about the culture of quality assurance. Since I don't use enough old english in my posts, I want to kick-off the topic with a couple of &lt;b&gt;QA Commandments &lt;/b&gt;that when recognized by all members of the community ensure a yummy culture of quality. The first part involves bug reporting, and they go something like this:&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;"&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;"&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;"&gt;&lt;b&gt;1.&lt;/b&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;"&gt;&lt;b&gt;Thou shalt report every bug, big and small, content, documentation, infrastructure, and beyond&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;"&gt;Is it a spelling mistake? Report it. Is the interface about as useful as a bag of sand? Report it. Is it impossible to find the documentation you are looking for? Report it!&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;"&gt;&lt;br /&gt;&lt;/span&gt; &lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;"&gt;Will your bug get fixed in a timely manner? Probably not. But that's ok. The point is that you noticed something that was below perfection, and you cared enough to document it by writing a bug report.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;"&gt;&lt;br /&gt;&lt;/span&gt; &lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;"&gt;I worked on a QA team right out of college and I am proud to say we worked just as hard (if not more!) as the developers. There was constant&amp;nbsp;arguing between the teams&amp;nbsp;but together we delivered a super stable end product. A key component of our strategy was competition within the QA team itself over who could report the most bugs. There was no prize, just pride. And it worked! Every nook and cranny of that system that wasn't perfect was documented.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;"&gt;&lt;br /&gt;&lt;/span&gt; &lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;"&gt;&lt;b&gt;2. Thou shalt not bitch about bug reports&lt;/b&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;"&gt;This follows from commandment 1.&amp;nbsp;Good QA people care even&amp;nbsp;&lt;/span&gt;&lt;b&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;"&gt;more&lt;/span&gt;&lt;/b&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;"&gt;&amp;nbsp;than the end user about a perfect experience, and caring takes a lot of effort. The thing that happens when developers stop complaining about bug reports is that it actually encourages people to report more bugs - imagine that!&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;"&gt;&lt;br /&gt;&lt;/span&gt; &lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;"&gt;A good set of bug reports should be treated like problem documentation. The better documented the faults are the easier it is to fix and the easier it is to provide support until that fix goes through (since it may be a while).&amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;"&gt;&lt;br /&gt;&lt;/span&gt; &lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;"&gt;The best part is that it's easy to encourage this culture. The next time you close a bug, big or small, thank the original reporter.&amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;"&gt;&lt;br /&gt;&lt;/span&gt; &lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;"&gt;&lt;b&gt;3. Thou shalt make it easy&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;"&gt;&lt;b&gt;for everyone&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;"&gt;&lt;b&gt;to report bugs&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;"&gt;Yes you! If you want to find lots of bugs you need to make it easy to report them. This &lt;/span&gt;&lt;a href="http://elizabethleddy.blogspot.com/2008/04/testc.html"&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;"&gt;makes me nuts&lt;/span&gt;&lt;/a&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;"&gt; with just about every software product known to man. One click to report a bug. No screens, no searching, just get the info and get them outta there. And yes, it's on my list of things to change with Plone.org.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;"&gt;&lt;br /&gt;&lt;/span&gt; &lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;"&gt;Unfortunately, I think Plone unnecessarily suffers from the bug collecting of zope and zodb. Searching for a zope bug collector without the exact keywords in google returns two bug collectors, one from 2003 and one from 2005. Both abandoned. And if you report in the wrong one they just close the ticket and that's it. Screw you end user for not being able to differentiate between a plone, zope, or zodb bug. Boo.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;"&gt;&lt;br /&gt;&lt;/span&gt; &lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;"&gt;So I am putting a list of collectors here for my reference as well as yours. Go ahead, report that bug you've been sitting on for a while.&amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://dev.plone.org/plone/newticket"&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;"&gt;Plone bug&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://dev.plone.org/plone.org/newticket"&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;"&gt;Plone.org bug&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://dev.plone.org/plone/newticket?component=Documentation"&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;"&gt;Plone Documentation Bug&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://bugs.launchpad.net/zope2"&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;"&gt;Zope2 Bug&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://bugs.launchpad.net/zope-pas/"&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;"&gt;PAS&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://bugs.launchpad.net/zope-cmf"&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;"&gt;CMF Bug&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://bugs.launchpad.net/zodb"&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;"&gt;ZODB Bug&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;"&gt;Zope3 -&amp;gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://bugs.launchpad.net/bluebream"&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;"&gt;Bluebream&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;"&gt;Now, if a bug gets marked invalid because it's someone else's problem you can just move on to the next tracker and follow through there. Did I miss any?&amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;"&gt;&lt;br /&gt;&lt;/span&gt; &lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;"&gt;&lt;b&gt;4. Thou shalt stand by that bug and not be bullied by&amp;nbsp;developers&lt;/b&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;"&gt;This is the hardest part of any QA effort. It's easy to give up when a developer spouts some mojo that you just don't understand.&amp;nbsp;Nonsense&amp;nbsp;poopy pants! Good QA people are so annoying developers wish they would go bury themselves in a hole in a forrest. And you know what? They are often wrong. But a lot of the time they are right, and a HUGE bug manifests from what seems like a template error. That's when it pays off to be a PIA.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;"&gt;&lt;br /&gt;&lt;/span&gt; &lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS', sans-serif;"&gt;So stick to your guns QA jedi's and do what's right for the end user. If you always keep them in mind the end result will always be what's best for the product and the community.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Lucida Grande';"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3585841838339962909-4343527001361841961?l=plonechix.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plonechix.blogspot.com/feeds/4343527001361841961/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plonechix.blogspot.com/2010/07/culture-of-reporting-bugs.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3585841838339962909/posts/default/4343527001361841961'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3585841838339962909/posts/default/4343527001361841961'/><link rel='alternate' type='text/html' href='http://plonechix.blogspot.com/2010/07/culture-of-reporting-bugs.html' title='The Culture of Reporting Bugs'/><author><name>eleddy</name><uri>http://www.blogger.com/profile/12829844320869028742</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_3RIl2f8RMJQ/R_ZvRpo1IJI/AAAAAAAAB3Y/8t5B8YVPTFc/S220/DSC00141.JPG.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3585841838339962909.post-2382095260916744477</id><published>2010-06-10T13:20:00.000-07:00</published><updated>2010-06-10T14:25:29.118-07:00</updated><title type='text'>Diversity in IRC</title><content type='html'>Confession: I was a closet female for a long time. My nick is purposefully&amp;nbsp;ambiguous&amp;nbsp;and when people assumed I was 'Eric' I never corrected them. I never signed my full name, not because I was afraid or insecure in my answer, but because I was afraid I would get treated differently for it. None of my profiles had my picture until just a few months ago, when I "came out" at the Plone Budapest conference.&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;div&gt;&lt;a href="http://4.bp.blogspot.com/_3RIl2f8RMJQ/TBE1wPREvdI/AAAAAAAAIWA/jlbr0yrA5NM/s1600/areyouahotchick.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="400" src="http://4.bp.blogspot.com/_3RIl2f8RMJQ/TBE1wPREvdI/AAAAAAAAIWA/jlbr0yrA5NM/s400/areyouahotchick.jpg" width="187" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;I usually feel bad about it. For 7 years I could have been supporting women in technology. It seemed the easiest way to get by at the time. Was I being a coward? Maybe I was making it all up?&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Then there are days like today, where a I get a gentle reminder as to where it all started. To the left is a screenshot of an #plone IRC chat with someone who was just completely lost in their task (probably still is). Many of us were helping out this person and at some point they moved it to a private conversation. Fine - that happens a lot.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;Click the image to get a "x-large" more readable version. I'll wait...&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I'm not posting this to complain* - in fact I see it as a victory. I have no hard feelings and a&amp;nbsp;simple correction not only took us back to business but raised the standard for getting help from that point forward.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Rather, I want people to understand why it's important for groups like PloneChix to exist. Diversity is scarce in the community and dealing with it is still new to many people. And guess what? The only way to make things better is to be more visible and pull through awkward moments like this.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;It's easy to dismiss this as "not a Plone problem" when it's plone "users" that are the offenders but that is a cop out. If someone is new to helping out on IRC and is treated like this, we risk permanently losing their help and expertise. If it's normal to have women in IRC, at conferences, etc... then the standard for behavior will be higher to begin with.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;So ladies, help me out - I'm tired of dealing with this alone. Take a moment to be seen: get in IRC, go to a conference, talk at a local meetup and invite your friends. One at a time, we can make Plone a stress free place for everyone.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;*I spend way too much time&amp;nbsp;arguing with people about whether or not this stuff still exists. Many think that it's a problem of the past because it doesn't happen in public forums or chats anymore. My response is usually "Duh! Of course it doesn't." Most incidents DO go unreported for that exact reason.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3585841838339962909-2382095260916744477?l=plonechix.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plonechix.blogspot.com/feeds/2382095260916744477/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plonechix.blogspot.com/2010/06/diversity-in-irc.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3585841838339962909/posts/default/2382095260916744477'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3585841838339962909/posts/default/2382095260916744477'/><link rel='alternate' type='text/html' href='http://plonechix.blogspot.com/2010/06/diversity-in-irc.html' title='Diversity in IRC'/><author><name>eleddy</name><uri>http://www.blogger.com/profile/12829844320869028742</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_3RIl2f8RMJQ/R_ZvRpo1IJI/AAAAAAAAB3Y/8t5B8YVPTFc/S220/DSC00141.JPG.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_3RIl2f8RMJQ/TBE1wPREvdI/AAAAAAAAIWA/jlbr0yrA5NM/s72-c/areyouahotchick.jpg' height='72' width='72'/><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3585841838339962909.post-4068600705860906227</id><published>2010-06-08T12:28:00.000-07:00</published><updated>2010-06-08T12:28:52.166-07:00</updated><title type='text'>Searching the Plone3 API</title><content type='html'>After kvetching about the Plone API documentation, I realized that maybe it's not the docs that need updated, but rather just a better way to navigate through them. Inspired by an &lt;a href="http://dukebody.com/2009/09/search-plugin-for-ploneorg-documentation/"&gt;awesome article by dukebody&lt;/a&gt;, I looked into how chrome could better serve me. To set up API search in the url bar:&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Chrome &amp;gt; Preferences &amp;gt; Default Search: Manage&lt;/li&gt;&lt;li&gt;Click the + sign&lt;/li&gt;&lt;li&gt;Choose your name and keyword. I used 'Plone 3 API' and 'api'. Use this beast for the url: http://www.google.com/search?hl=en&amp;amp;q=site:http://api.plone.org/Plone/3.0/private/products/+%s&amp;amp;aq=f&amp;amp;aqi=&amp;amp;aql=&amp;amp;oq=&amp;amp;gs_rfai=&lt;/li&gt;&lt;li&gt;Save and enjoy.&lt;/li&gt;&lt;/ol&gt;&lt;div&gt;In my case to use it I just enter 'api PloneModuleName' into the url bar and click enter. This gives me instant gratification and filters out the weeds of versions I don't want. Plone on.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3585841838339962909-4068600705860906227?l=plonechix.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plonechix.blogspot.com/feeds/4068600705860906227/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plonechix.blogspot.com/2010/06/searching-plone3-api.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3585841838339962909/posts/default/4068600705860906227'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3585841838339962909/posts/default/4068600705860906227'/><link rel='alternate' type='text/html' href='http://plonechix.blogspot.com/2010/06/searching-plone3-api.html' title='Searching the Plone3 API'/><author><name>eleddy</name><uri>http://www.blogger.com/profile/12829844320869028742</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_3RIl2f8RMJQ/R_ZvRpo1IJI/AAAAAAAAB3Y/8t5B8YVPTFc/S220/DSC00141.JPG.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3585841838339962909.post-3561784602710958387</id><published>2010-05-18T10:54:00.000-07:00</published><updated>2010-05-18T10:54:58.479-07:00</updated><title type='text'>Adventures in Buildout: Zope 2.12 meets SOAP</title><content type='html'>After 3 years of hanging around in Plone 2.5, I am finally getting around to upgrading our infrastructure to Plone 4. Part of that upgrade includes pulling out all of our services and api calls into a&amp;nbsp;separate&amp;nbsp;buildout, a solution that does not actually need the Plone package. A big problem with this migration was that we still use SOAP to talk to external vendors, most python SOAP modules suck, and none of them are fully integrated with a database/framework.&lt;br /&gt;&lt;br /&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;As a satisfied user of&amp;nbsp;&lt;a href="http://www.zope.org/Members/Dirk.Datzert/SOAPSuppport"&gt;SOAPSupport&lt;/a&gt;&amp;nbsp;for many years, my first inclination was just to continue with an old version of Zope and continue to use that. &amp;nbsp;However, the project hasn't been supported/updated in a long time and I was excited to take advantage of memory improvements in Python 2.6, a fully eggified Zope, and Blob storage among other things. There is&amp;nbsp;a great article on&amp;nbsp;&lt;a href="http://plone.org/documentation/kb/soap-support-for-plone"&gt;configuring SOAP in Plone&lt;/a&gt;, which targets Zope 2.11 and custom products, however its still very mind bending for those of us with simpler needs.&lt;/div&gt;&lt;br /&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;While updating the z3c.soap package for Zope 2.12 compatability, I was also busy trying to grok "new" buildout concepts (I know - I've been in the closet for a while). I couldn't find one really solid simple tutorial on getting started with buildout. After a roller coaster ride of emotions and awesome help from the Plone community, I *think* I've figured out the simplest way to get any python SOAP server up and running with (or without) buildout. I have not addressed the WSDL or complex types questions yet, since I don't need them. I also said simplest, which means I had little regard for proper classing and blah blah blah... yell at me in the comments if it's grossly off-key or if I missed something - it's bloody likely.&lt;/div&gt;&lt;br /&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;Below is an introduction to configuring SOAP in Zope as well as an extra detailed buildout process for old-schoolers like myself who are used to Products based install. I hope it helps someone get up and running with a python soap server fast, in a repeatable, buildout style.&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;FYI, this can also easily be customized to support Plone and other zope based apps since its buildout based. &amp;nbsp;My goal was to make this like&amp;nbsp;&lt;a href="http://plone.org/products/mysite"&gt;MySite&lt;/a&gt;, where you can start and build off of it. In the end I don't think I have even come close the greatness that was MySite but you can still &lt;a href="http://plonechix.pbworks.com/f/zsoap.zip"&gt;download the finished package&lt;/a&gt;&amp;nbsp;if you don't care about the how and just want the now, or if you want to follow along easier.&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;NOTE: At the time of writing virtualenv has a bug with this buildout so if you get an ImportError for shutil, just use regular old python. This is also why this tutorial is not using virtualenv, although its perfect for this scenario otherwise.&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Summary&lt;/span&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;For those who don't care about buildout or the details, &lt;a href="http://plonechix.pbworks.com/ZopeSoapBuilout"&gt;here is the&amp;nbsp;recipe&lt;/a&gt;&amp;nbsp;for simple zope + soap. Customize away!&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;The Details: Creating the Buildout&lt;/span&gt;&lt;/div&gt;I won't go into the philosophy of buildout since its all over so we can get to implementing. I keep all of my buildouts in a directory in my home folder, called 'buildouts', so I always know where to start. Also, before you get to the end and kick yourself, I'm using Python 2.6 here. I have not tested it with any other version. Caveat Emptor.&lt;br /&gt;&lt;br /&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;First, make sure that you have the buildout package installed:&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;pre&gt;&amp;gt; sudo easy_install zc.buildout&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;Create a buildout environment, with all the scripts we need to get started&lt;/div&gt;&lt;/div&gt;&lt;pre&gt;# create a new directory for our new buildout&lt;br /&gt;&amp;gt; cd ~/buildouts&lt;br /&gt;&amp;gt; mkdir zsoap&lt;br /&gt;&amp;gt; cd zsoap&lt;br /&gt;# initialize the buildout environment&lt;br /&gt;&amp;gt; buildout init&lt;br /&gt;Creating '/Users/eleddy/buildouts/zsoap/buildout.cfg'.&lt;br /&gt;Creating directory '/Users/eleddy/buildouts/zsoap/bin'.&lt;br /&gt;Creating directory '/Users/eleddy/buildouts/zsoap/parts'.&lt;br /&gt;Creating directory '/Users/eleddy/buildouts/zsoap/eggs'.&lt;br /&gt;Creating directory '/Users/eleddy/buildouts/zsoap/develop-eggs'.&lt;br /&gt;Generated script '/Users/eleddy/buildouts/zsoap/bin/buildout'.&lt;br /&gt;# optional: include bootstrap.py&lt;br /&gt;&amp;gt; wget http://svn.zope.org/*checkout*/zc.buildout/trunk/bootstrap/bootstrap.py&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;You don't have to include the bootstrap.py file, but it makes things hella easier and I'll assume that you have it for the rest of this. After&amp;nbsp;initializing&amp;nbsp;the buildout, you will have a bunch of extra directories in your zsoap folder, including bin, buildout.cfg, develop-eggs, eggs, and parts. Don't worry about them for now - just make sure they are there.&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;Next, edit buildout.cfg so it looks like&amp;nbsp;&lt;a href="http://plonechix.pbworks.com/ZopeSoapBuilout"&gt;this&lt;/a&gt;. I'll explain it all while we wait for the buildout to run.&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;Finally run the buildout:&lt;/div&gt;&lt;/div&gt;&lt;pre&gt;&amp;gt; python bootstrap.py&lt;br /&gt;&amp;gt; ./bin/buildout&lt;br /&gt;&lt;/pre&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;Note that whatever version of python you run bootstrap with is the version that zope will be running with. This recipe was created in Python 2.6, since Zope 2.12 runs swimmingly on it. If your system python is not 2.6, make sure to alter your bootstrap command to reflect that.&amp;nbsp;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;While its running, you may see errors like "SyntaxError: ("'return' outside function",...".&amp;nbsp;Don't worry about those. It's simply trying to compile scripts, which you can't do.&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;The Breakdown&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;Once the buildout starts going, go ahead and take a shower, walk the dog, or read the line by line description of what is happening:&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;1. [buildout]&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;2. parts = scripts&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;3. &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; instance&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;4. &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; test&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;5.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;6. extends = http://svn.zope.org/*checkout*/Zope/tags/2.12.5/versions.cfg&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;7. versions = versions&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;8. eggs = z3c.soap&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;9. extensions = buildout.threatlevel&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;Lines 1-4 say that this buildout has three parts that need to be run. When you run this, you will see that the "parts" directory that was automagically created from initializing the environment now has 3 folders with the exact same names. Coincidence? You decide.&amp;nbsp;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;Line 6 is a link to download an automatically configured buildout for installing zope 2 in a delicious eggy format. To change versions of zope, just modify this url. Go ahead.&amp;nbsp;&lt;a href="http://svn.zope.org/*checkout*/Zope/tags/2.12.5/versions.cfg"&gt;Try it&lt;/a&gt;.&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;Line 7 Indicates that we are going to force some packages to get a certain version, instead of the latest. This is called "pinning" a version. Some packages have conflicting versions, and you may need to pin the version which satisfies everything. This line simply tells buildout that there is a section, called versions, which will do just that.&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;Line 8 indicates which additional eggs we need to get the project going. It will ask&amp;nbsp;&lt;a href="http://pypi.python.org/pypi"&gt;PyPi&lt;/a&gt;&amp;nbsp;for the latest version of this eggs (unless we pinned it) and install them. Notice that we didn't have to include Zope2 because it's handled with the scripts section with a special recipe that you'll see later.&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;10. [versions]&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;11. Zope2 = 2.12.5&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;12. &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;10-11 show how to pin a version of a package. This is not necessary for this case since the Zope2 recipe does this for us, but other packages probably won't have this luxury. Maybe you want to pin to a minor version, for example. Furthermore I already numbered these lines and I'm too lazy to go through and renumber them.&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&lt;span class="Apple-style-span" style="font-family: Times;"&gt;&lt;span class="Apple-style-span" style="font-size: medium;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;13. [scripts]&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;14. recipe = zc.recipe.egg:scripts&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;15. eggs = Zope2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;Lines 13-15 might as well be magic. They install Zope2, the inner workings of which I have no idea.&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&lt;span class="Apple-style-span" style="font-family: Times;"&gt;&lt;span class="Apple-style-span" style="font-size: medium;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;16. [instance]&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;17. recipe = plone.recipe.zope2instance&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;18. user = admin:admin&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;19. http-address = 8081&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;20. products = ${buildout:directory}/products&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;21. debug-mode=on&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;22. zcml = z3c.soap&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;23. eggs = ${buildout:eggs}&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;Lines 16-23 create a new zope2 instance. It's pretty self-explanatory except for a couple lines. Line 22 males sure that we include the z3c.soap package, and run the zcml slug that initializes it. This patches the publisher to make it accept soap requests. Line 23 makes sure that all the eggs created in other places of this buildout are put in the path of the instance. Without this, it won't be able to import anything.&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;24. [test]&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;25. recipe = zc.recipe.testrunner&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;26. eggs = z3c.soap&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;And finally, we must not forget to include test cases to make sure everything is running smashingly.&amp;nbsp;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Up and Running&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;Assuming everything runs correctly, we can start testing things out. First we want to validate that everything looks like its running ok.&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;pre&gt;&amp;gt; ./bin/test&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;It's that easy. This should run fine and pass all tests. Yeehaw! Let's start the new zope 2 instance in&amp;nbsp;foreground&amp;nbsp;mode:&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;pre&gt;&amp;gt; ./bin/instance fg&lt;br /&gt;&lt;/pre&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;Still easy. Woohoo! If the slug was installed&amp;nbsp;correctly, z3c.soap will have a line in the trackback that says something like "INFO Zope z3c.soap: modified ZPublisher.HTTPRequest.processInputs". Congratulations, you are ready to serve SOAP requests.&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Taking it a Step Awesomer&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;Now what? Well, you can follow the&amp;nbsp;&lt;a href="http://plone.org/documentation/kb/soap-support-for-plone"&gt;original tutoria&lt;/a&gt;l and add wsdls and custom type and all that fancy stuff. Since I'm lazy I prefer to do everything through scripts. In the ZMI, I like to add a folder called 'services' and then just pile up python scripts in there. Err... I mean Script (Python)'s. Let's verify it's all SOAPed up like we want. Add a script called 'test' in a folder called 'scripts' that simply returns a string 'hello world!'. Then you can use the&amp;nbsp;&lt;a href="http://sourceforge.net/projects/pywebsvcs/files/SOAP.py/"&gt;SOAPpy&lt;/a&gt;&amp;nbsp;package to see what's really going on. From the python prompt:&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; from SOAPpy import SOAPProxy, URLopener&lt;br /&gt;  &amp;gt;&amp;gt;&amp;gt; url = "http://localhost:8081/services"&lt;br /&gt;  &amp;gt;&amp;gt;&amp;gt; namespace = "http://plone.org"&lt;br /&gt;  &amp;gt;&amp;gt;&amp;gt; server = SOAPProxy(url)&lt;br /&gt;  &amp;gt;&amp;gt;&amp;gt; server.config.dumpSOAPOut = 1&lt;br /&gt;  &amp;gt;&amp;gt;&amp;gt; server.config.dumpSOAPIn = 1&lt;br /&gt;  &amp;gt;&amp;gt;&amp;gt; print server._ns(namespace).test()[0]&lt;br /&gt;  hello world!&lt;br /&gt;&lt;/pre&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;Every script you add will have a SOAP interface built in. How cool is that? Also, a tip from the trenches, use dictionaries to pass back values for more complex values. In my experience, it's more compatible with Java stacks, which I assume you have to deal with, because otherwise you wouldn't be using SOAP. I digress...&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;So I know you're thinking, what is the awesomer part of "taking it a step awesomer"? Well, if you want a repeatable buildout you don't want to store your scripts in the ZMI. I know I didn't, since we have an SVN repository that is way better for such things (again, I'm behind, I'm not cool enough to Git or Hg yet). We can do is use the CMF Filesystem Directory view and store all of our scripts on the filesystem. While they are not editable in the ZMI, but the can be executed and customized.&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;I tried my hardest to find a way to do this without making a product, but I can't find a way to register a CMF directory view without being in the context of a product. So let's walk through making one - you probably need it anyways. It's slightly annoying, but at least this way it won't get wiped by re-running buildout. If you already have a product then skip the paster part and just add the necessary slugs.&lt;br /&gt;&lt;br /&gt;First make sure that you have access to paster. If you can't use paster from the command line,&lt;br /&gt;&lt;pre&gt;&lt;/pre&gt;&lt;pre&gt;&amp;gt; sudo easy_install -U ZopeSkel&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Then decide a name for the product, and run paster. I will use z.soap for this purpose.&lt;br /&gt;&lt;pre&gt;&lt;/pre&gt;&lt;pre&gt;&amp;gt; cd develop-eggs&lt;br /&gt;&amp;gt; paster create -t basic_zope&lt;br /&gt;&amp;gt; ...blah blah blah&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Next we need to add our new egg to the buildout.cfg, mark it as a development package, and include it in our zope instance. Update your buildout.cfg to look like &lt;a href="http://plonechix.pbworks.com/ZopeSoapBuilout2"&gt;this&lt;/a&gt;, with the bold lines indicating the changes. Hopefully by now the buildout thing is starting to make sense. But we aren't done yet.&lt;br /&gt;&lt;br /&gt;Now we need to actually register a services directory. Let's make the directories first&lt;br /&gt;&lt;pre&gt;&amp;gt; mkdir ~/buildouts/zsoap/develop-eggs/z.soap/z/soap/skins&lt;br /&gt;&amp;gt; mkdir ~/buildouts/zsoap/develop-eggs/z.soap/z/soap/skins/services&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;All of the Script (Python)'s that we want to be version controlled can go in the services directory. You can version control the whole buildout, or of course register your new egg with PyPi and replace that whole mess with eggs = my.egg.&lt;br /&gt;&lt;br /&gt;Tell zope about the directory that you created by editing your new eggs configure.zcml, which will be in ~buildouts/zsoap/develop-eggs/z.soap/z/soap/configure.zcml. Add the registerDirectory directive to point to the folder you just created, adding the cmf namespace if needed. The final configure.zcml should look like &lt;a href="http://plonechix.pbworks.com/ConfigureZCMLZopeSoap"&gt;this&lt;/a&gt;, with the changes highlighted.&lt;br /&gt;&lt;br /&gt;Phew! Are we done yet? No. Re-run buildout and restart your instance&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;gt; ./bin/buildout&lt;br /&gt;&amp;gt; ./bin/instance fg&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;At the root of the ZMI, add a Filesystem Directory View and you will see that your directory is listed. Let's put the same test script that was in the zodb the first time in the filesystem now. In your new skins, services directory, add a file, test.py with the contents below and run the SOAPpy test above. Viola! Now you can add scripts until you are sick. They can compute, forward info, act as an API, and even interact with your products. No restart needed to add/change scripts since its a filesystem view.&lt;br /&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;/div&gt;&lt;pre style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;##Script (Python) "test"&lt;br /&gt;##bind container=container&lt;br /&gt;##bind context=context&lt;br /&gt;##bind namespace=&lt;br /&gt;##bind script=script&lt;br /&gt;##bind subpath=traverse_subpath&lt;br /&gt;##parameters=&lt;br /&gt;##title=&lt;br /&gt;##&lt;br /&gt;return "hello world!"&lt;/pre&gt;&lt;br /&gt;But wait, you don't want to do that last step manually? You want that automated? I did too! Last but not least, let's add the directory view on startup if it's not already there. There is a special initialize function that we can call on startup. Open up&amp;nbsp;develop-eggs/z.soap/z/soap/__init__.py and make it look like &lt;a href="http://plonechix.pbworks.com/ZopeSoapInitialize"&gt;this&lt;/a&gt;. Now instead of calling zope2 initialization, we need to call our own package initializer by modifying the configure.zcml to look like &lt;a href="http://plonechix.pbworks.com/ConfirgureZCMLZopeSoap2"&gt;this&lt;/a&gt;.&amp;nbsp;Now start up the zope&amp;nbsp;instance&amp;nbsp;and it will come alive!&lt;br /&gt;&lt;br /&gt;So, if there is anything wrong here or it could be done easier, please comment and I will update. The last part was definitely not as easy to setup as I would have hoped. And because I'm so nice, you can &lt;a href="http://plonechix.pbworks.com/f/zsoap.zip"&gt;download this package&lt;/a&gt;, untar, bootstrap, build, and go crazy with customizing.&amp;nbsp;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3585841838339962909-3561784602710958387?l=plonechix.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plonechix.blogspot.com/feeds/3561784602710958387/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plonechix.blogspot.com/2010/05/adventures-in-buildout-zope-212-meets.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3585841838339962909/posts/default/3561784602710958387'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3585841838339962909/posts/default/3561784602710958387'/><link rel='alternate' type='text/html' href='http://plonechix.blogspot.com/2010/05/adventures-in-buildout-zope-212-meets.html' title='Adventures in Buildout: Zope 2.12 meets SOAP'/><author><name>eleddy</name><uri>http://www.blogger.com/profile/12829844320869028742</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_3RIl2f8RMJQ/R_ZvRpo1IJI/AAAAAAAAB3Y/8t5B8YVPTFc/S220/DSC00141.JPG.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3585841838339962909.post-8694673486144719455</id><published>2010-02-22T17:51:00.000-08:00</published><updated>2010-02-24T15:05:02.218-08:00</updated><title type='text'>Catalog Key Errors</title><content type='html'>Apparently I am the patron saint of &lt;a href="http://plonechix.blogspot.com/2009/12/definitive-guide-to-poskeyerror.html"&gt;key errors&lt;/a&gt;. The latest one was with the catalog, and it was way nicer than some of the previous monsters because at least it didn't bring down the whole site or involve sweaty, hand shaking stress. To the end user, certain keyword searches didn't in livesearch, advanced queries. This key error manifested in the logs as:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;Module Products.ZCTextIndex.BaseIndex, line 203, in search_phrase&lt;br /&gt;  Module Products.ZCTextIndex.OkapiIndex, line 161, in _search_wids&lt;br /&gt;KeyError: -1525983394&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I had a feeling that this would happen because a few days earlier, I came across a few weird errors trying to emply some jedi mind tricks in the db. An example of one:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;2010-02-17 21:46:36,505 ERROR @/opt/Zope-2.9/lib/python/Products/PluginIndexes/common/UnIndex.py/UnIndex.py UnIndex 194  : DateIndex: unindex_object could not remove documentId -1324564720 from index modified.  This should not happen.&lt;br /&gt;Traceback (most recent call last):&lt;br /&gt; File "/opt/Zope-2.9/lib/python/Products/PluginIndexes/common/UnIndex.py", line 168, in removeForwardIndexEntry&lt;br /&gt;   indexRow.remove(documentId)&lt;br /&gt;KeyError: -1825924534&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Whoops.&amp;nbsp; They didn't cause anything obvious to happen so I did what every lazy sys admin does and ignored it. As far as I can tell there isn't a required correlation between the two errors, but it will at least point you in the right direction.&lt;br /&gt;&lt;br /&gt;That was Friday night. Then customers got on the system Monday and we started getting the ZCTextIndex KeyError sporadically. It affected all searches with the word 'jackson' and using indexes set up with OkapiIndex algorithm (PloneLexicon). This is the default choice for the Title, SearchableText, and Description indexes.&lt;br /&gt;&lt;br /&gt;Looking this error up on Google shows a few answers, which involve &lt;a href="http://markmail.org/message/iupk5lo3bpr2wnow#query:Products.ZCTextIndex.OkapiIndex%20KeyError+page:1+mid:v5dv44gmbhzhabzn+state:results"&gt;clearing and rebuilding&lt;/a&gt; the catalog. I'm sure this works, but our index alone is 1.2GB so that was really a last resort option. It would take so long that before reindexing we probably just would have monkeypatched the catalog code to just ignore those keys. I was determined to find a way to fix without rebuilding. And I did.&lt;br /&gt;&lt;br /&gt;Warning: the following explanation is based on eye crossing interpretations of zope code. I may be off a little but I think its a worthy attempt nonethless.&lt;br /&gt;&lt;br /&gt;The text indexes need a way to correlate a word set (what the user is searching for) with a document and vice-versa.&amp;nbsp; For example, when I lookup 'jackson', I need to know that 17 documents have that keyword. If you have been mucking around with the catalogs or something went crazy on your catalog (like your sys admin) then you can get this words list of sync with other indexes and the real catalog. To confirm this, try idx.getEntryForObject(docid) on the failing index, where docid is the KeyError integer in your error log. In my example, the keyword 'jackson' in the words BTree pointed to a list of documents, all of which were valid except -1525983394. By just extracting the bad reference to this non-existing document, then there is no need to reindex everything.&lt;br /&gt;&lt;br /&gt;Note that for this to work you need to know the word that triggers the error. You can't do a reverse lookup to find out which words reference that document because that mapping is the one that is still correct. It will always return empty due to a silent fail. If your users won't tell you, then you can just log it when it errors out later. If its really bad, iterate through all words and what they point to and see if they throw a key error (hint: use the _wordinfo.iterkeys() iterator + other tools listed &lt;a href="http://docs.zope.org/zope3/Code/BTrees/IIBTree/IIBTree.1"&gt;here&lt;/a&gt;). Or just reindex.&lt;br /&gt;&lt;br /&gt;As always, backup before hand and if you are nervous work on a copy first. I'm sure this can easily be modified for other indexes/schemes as well.&lt;br /&gt;&lt;br /&gt;From the debug prompt:&lt;br /&gt;&lt;pre&gt;'''&lt;br /&gt;@docids is a list of the key error items as ints, not strings&lt;br /&gt;@word is the word that triggers the index error&lt;br /&gt;@cat is the catalog object that has the error, i.e. app.foo.portal_catalog&lt;br /&gt;'''&lt;br /&gt;def removeFromWordCatalog(docids, word, cat):&lt;br /&gt;    import transaction&lt;br /&gt;    for docid in docids:&lt;br /&gt;        for indexObj in cat.getIndexObjects():&lt;br /&gt;            try:&lt;br /&gt;                itype = indexObj.getIndexType()&lt;br /&gt;            except AttributeError:&lt;br /&gt;                continue # some indexes don't implement this method&lt;br /&gt;            lex = indexObj.getLexicon()&lt;br /&gt;            if itype == 'Okapi BM25 Rank':&lt;br /&gt;                wids = lex.termToWordIds(word)&lt;br /&gt;                idx = indexObj.index&lt;br /&gt;                for wid in wids:                &lt;br /&gt;                    try:&lt;br /&gt;                        # using excepts because BTrees key lookup is annoying&lt;br /&gt;                        # see if its even really an error before ousting&lt;br /&gt;                        blah = idx._wordinfo[wid][docid]&lt;br /&gt;                    except KeyError: # not listed - nothing wrong&lt;br /&gt;                        continue&lt;br /&gt;                        &lt;br /&gt;                    idx._wordinfo[wid].pop(docid)&lt;br /&gt;                    idx._wordinfo[wid] # persistance&lt;br /&gt;                    try: &lt;br /&gt;                        idx._wordinfo[wid][docid]&lt;br /&gt;                    except KeyError:&lt;br /&gt;                        print "sucessfully removed"&lt;br /&gt;                    transaction.savepoint(1)&lt;br /&gt;                    transaction.commit() &lt;br /&gt;&lt;br /&gt;docids = [-1525983376, -3423423445, ...]&lt;br /&gt;word = 'jackson'&lt;br /&gt;cat = app.foo.portal_catalog&lt;br /&gt;removeFromWordCatalog(docids, word, cat)&amp;nbsp;&lt;/pre&gt;&lt;br /&gt;UPDATE: I already got to the point where the word by word thing became annoying so I wrote a generic function to "unword" a catalog entry. Much nicer, and although I thought it would take a long time it was actually not toooooo bad. I also pieced out the code so I could get as generic or specific as I want in the case that something else happens. Which it will. Woohoo!&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;def removeDocFromWordlist(idx, wid, docid):&lt;br /&gt;    import transaction&lt;br /&gt;    try:&lt;br /&gt;        # using excepts because BTrees key lookup is annoying&lt;br /&gt;        # see if its even really an error before ousting&lt;br /&gt;        blah = idx._wordinfo[wid][docid]&lt;br /&gt;    except KeyError: # not listed - nothing wrong&lt;br /&gt;        return &lt;br /&gt;        &lt;br /&gt;    idx._wordinfo[wid].pop(docid)&lt;br /&gt;    idx._wordinfo[wid] # persistance&lt;br /&gt;    try: &lt;br /&gt;        idx._wordinfo[wid][docid]&lt;br /&gt;    except KeyError:&lt;br /&gt;        print "sucessfully removed"&lt;br /&gt;    transaction.savepoint(1)&lt;br /&gt;    transaction.commit() &lt;br /&gt;    &lt;br /&gt;    &lt;br /&gt;def cleanUpBadDocId(docid, idx):&lt;br /&gt;    iterati = idx._wordinfo.iterkeys()&lt;br /&gt;    while True:&lt;br /&gt;        try:&lt;br /&gt;            wid = iterati.next()&lt;br /&gt;        except StopIteration:&lt;br /&gt;            break&lt;br /&gt;        &lt;br /&gt;        if idx._wordinfo[wid].has_key(docid):&lt;br /&gt;            print "removing %s from word list %s"%(docid, wid)&lt;br /&gt;            removeDocFromWordlist(idx, wid, docid)&lt;br /&gt;            &lt;br /&gt;            &lt;br /&gt;def removeFromWordCatalogs(docids, cat):&lt;br /&gt;    for docid in docids:&lt;br /&gt;        for indexObj in cat.getIndexObjects():&lt;br /&gt;            try:&lt;br /&gt;                itype = indexObj.getIndexType()&lt;br /&gt;            except AttributeError:&lt;br /&gt;                continue # some indexes don't implement this method&lt;br /&gt;            lex = indexObj.getLexicon()&lt;br /&gt;            if itype == 'Okapi BM25 Rank':&lt;br /&gt;                idx = indexObj.index            &lt;br /&gt;                cleanUpBadDocId(docid, idx)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;docids = [-1525983376, -3423423445, ...]&lt;br /&gt;cat = app.foo.portal_catalog&lt;br /&gt;removeFromWordCatalogs(docids, cat)&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3585841838339962909-8694673486144719455?l=plonechix.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plonechix.blogspot.com/feeds/8694673486144719455/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plonechix.blogspot.com/2010/02/catalog-key-errors.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3585841838339962909/posts/default/8694673486144719455'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3585841838339962909/posts/default/8694673486144719455'/><link rel='alternate' type='text/html' href='http://plonechix.blogspot.com/2010/02/catalog-key-errors.html' title='Catalog Key Errors'/><author><name>eleddy</name><uri>http://www.blogger.com/profile/12829844320869028742</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_3RIl2f8RMJQ/R_ZvRpo1IJI/AAAAAAAAB3Y/8t5B8YVPTFc/S220/DSC00141.JPG.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3585841838339962909.post-5428145912117141469</id><published>2010-02-16T23:37:00.000-08:00</published><updated>2010-02-17T08:34:20.862-08:00</updated><title type='text'>PloneChix: Hot and Not</title><content type='html'>&lt;div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: Verdana, sans-serif;"&gt;Instead of boring you with a lengthy explanation of our official&amp;nbsp;&lt;a href="http://plonechix.pbworks.com/Our-Purpose"&gt;purpose&lt;/a&gt;, I'd like to address what makes PloneChix exciting and unique and then tackle some misconceptions about&amp;nbsp;what&amp;nbsp;people think PloneChix represents. As a followup post,&amp;nbsp;I'd like to invite all PloneChix members to explain&amp;nbsp;why&amp;nbsp;PloneChix exists for them. There will be more meaning and&amp;nbsp;inspiration&amp;nbsp;from their stories than reading an tedious political statement.&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Verdana, sans-serif;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Verdana, sans-serif;"&gt;What is so hot about PloneChix? PloneChix is ...&lt;/span&gt;&lt;br /&gt;&lt;div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&lt;span class="Apple-style-span" style="font-family: Verdana, sans-serif;"&gt;&lt;ul&gt;&lt;li&gt;... a no flame zone. That means no RTFMs, no stupid questions, no negative feedback, and no smack talk. This is the most important aspect of who we are. Women in technology, especially open source, are more likely to withdraw from a community because of actions/comments/answers perceived as harsh. &amp;nbsp; By making a special effort to make a no flame zone, we are encouraging people to ask for help and giving them helpful, thorough, honest answers by any means possible. Who knows, maybe all this kindness will be contagious.&amp;nbsp;&lt;/li&gt;&lt;li&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&lt;span class="Apple-style-span" style="font-family: Verdana, sans-serif;"&gt;... a place to be heard.&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&lt;span class="Apple-style-span" style="font-family: Verdana, sans-serif;"&gt;&amp;nbsp;Speak up, let us help you, stick around Plone for a little bit longer and we think you'll like what you see.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&lt;span class="Apple-style-span" style="font-family: Verdana, sans-serif;"&gt;... a place to ask non-technical questions (although we like technical too). For example: What is the job situation in the bay area for Plone integrators? Does company X have a supportive female developer community? What can I do to market myself better as an integrator/developer/documenter?&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&lt;span class="Apple-style-span" style="font-family: Verdana, sans-serif;"&gt;... an open and supportive learning environment. This is the perfect arena to tell a story and get honest feedback. Did you blow that interview because you underrepresented yourself or did you really not have enough experience? Let's talk about it. It's a place to get advice from women with different experiences and backgrounds.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&lt;span class="Apple-style-span" style="font-family: Verdana, sans-serif;"&gt;... an opportunity to tackle that project with a group instead of doing it on your own. We have just started working on defining our&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&lt;span class="Apple-style-span" style="font-family: Verdana, sans-serif;"&gt;&lt;a href="http://plonechix.pbworks.com/Project-Ideas"&gt;projects for this year&lt;/a&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&lt;span class="Apple-style-span" style="font-family: Verdana, sans-serif;"&gt;&amp;nbsp;Why not add yours? If you have wanted to commit to the core for a while but just didn't want to tackle such a huge project on your own or want some help, we'll make sure to hook you up with a veteran who will show you the ropes.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&lt;span class="Apple-style-span" style="font-family: Verdana, sans-serif;"&gt;... a place to network. PloneChix is here to share and encourage each other to write that first book or give a talk at that conference. In my opinion, visibility is the #1 issue with women in the Plone community. Be visible, be heard, and get your name out there. There are lots of special opportunities for women in technology, and if we don't take advantage of them women in other communities will (i.e. Ruby women are fierce). Posting job opportunities, calls for papers/talks, scholarships, et al is highly encouraged.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&lt;span class="Apple-style-span" style="font-family: Verdana, sans-serif;"&gt;Hot indeed.&amp;nbsp;There are already some strange rumors&amp;nbsp;about PloneChix that have surfaced that I'd like to take a moment to address. PloneChix is NOT...&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;ul&gt;&lt;li&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&lt;span class="Apple-style-span" style="font-family: Verdana, sans-serif;"&gt;... a group of man-hating femi-nazis. In fact it would be way easier to justify if we were. However, we do have a&amp;nbsp;tendency&amp;nbsp;to focus on the needs of women and womens issues in the Plone, python, and Open Source Software (OSS) communities.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&lt;span class="Apple-style-span" style="font-family: Verdana, sans-serif;"&gt;... a derogatory name. It's an homage to&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href="http://www.linuxchix.org/about-linuxchix.html"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&lt;span class="Apple-style-span" style="font-family: Verdana, sans-serif;"&gt;LinuxChix&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&lt;span class="Apple-style-span" style="font-family: Verdana, sans-serif;"&gt;,&amp;nbsp;one of the finest female-friendly OSS movements to this day. There are many other groups that pay tribute to the original, including DevChix, DrupalChix and CodeChix, just to name a few. The word "Chix" has become synonymous with female fronted groups that discourage friendly fire (no RTFMs) and encourage low barrier to entry.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&lt;span class="Apple-style-span" style="font-family: Verdana, sans-serif;"&gt;... a "women only" club. Anyone is welcome as long as they are supportive of the cause and contribute positively back to it and/or the Plone community.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&lt;span class="Apple-style-span" style="font-family: Verdana, sans-serif;"&gt;... a support group for complaining or flaming, especially about other members of the community. PloneChix is a place for positive feedback, encouragement, and help. We are about positive change, not negative reinforcement.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&lt;span class="Apple-style-span" style="font-family: Verdana, sans-serif;"&gt;... looking for 50:50 male to female ratio just for the sake of having a 50:50 male to female ratio. On the contrary, we are all interested in seeing the entire Plone community grow and thrive in the way that&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href="http://infotrope.net/blog/2009/07/25/standing-out-in-the-crowd-my-oscon-keynote/"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&lt;span class="Apple-style-span" style="font-family: Verdana, sans-serif;"&gt;Kirrily Roberts&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&lt;span class="Apple-style-span" style="font-family: Verdana, sans-serif;"&gt;* describes.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&lt;span class="Apple-style-span" style="font-family: Verdana, sans-serif;"&gt;... a replacement for the mailing list, irc, or other more formal means of communication about Plone. It is an additional communication channel lead by like-minded individuals. It is also a place to talk about careers, meetups, projects, or even just to brag about a new project in a friendly, open environment.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: Verdana, sans-serif;"&gt;This is the part of the post where I start practicing what I've been preaching. Girls, boys, aliens, machines: how does this make you feel? Hint: I was shooting for all warm and tingly inside.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&lt;span class="Apple-style-span" style="font-family: Verdana, sans-serif;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&lt;span class="Apple-style-span" style="font-family: Verdana, sans-serif;"&gt;If you are interested in participating, male or female, check out&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&lt;span class="Apple-style-span" style="font-family: Verdana, sans-serif;"&gt;&lt;a href="http://plonechix.pbworks.com/"&gt;our wiki&lt;/a&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&lt;span class="Apple-style-span" style="font-family: Verdana, sans-serif;"&gt;&amp;nbsp;which has all the information you need to get started.&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&lt;span class="Apple-style-span" style="font-family: Verdana, sans-serif;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&lt;span class="Apple-style-span" style="font-family: Verdana, sans-serif;"&gt;* Please read this post 50 times. Take a nap. Then 50 more. Then we'll talk.&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3585841838339962909-5428145912117141469?l=plonechix.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plonechix.blogspot.com/feeds/5428145912117141469/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plonechix.blogspot.com/2010/02/plonechix-hot-and-not.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3585841838339962909/posts/default/5428145912117141469'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3585841838339962909/posts/default/5428145912117141469'/><link rel='alternate' type='text/html' href='http://plonechix.blogspot.com/2010/02/plonechix-hot-and-not.html' title='PloneChix: Hot and Not'/><author><name>eleddy</name><uri>http://www.blogger.com/profile/12829844320869028742</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_3RIl2f8RMJQ/R_ZvRpo1IJI/AAAAAAAAB3Y/8t5B8YVPTFc/S220/DSC00141.JPG.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3585841838339962909.post-1572153881473346491</id><published>2009-12-02T18:31:00.000-08:00</published><updated>2010-02-05T16:52:35.752-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='POSKeyError'/><category scheme='http://www.blogger.com/atom/ns#' term='errors'/><category scheme='http://www.blogger.com/atom/ns#' term='plone'/><title type='text'>The Definitive Guide to POSKeyError</title><content type='html'>Trying to get PloneChix off the ground during the holidays has been a tough start so I figured we could get the blog a good head start with a much needed post for anyone who has had the hair pulling, spirit breaking chance to work with a POSKeyError. Happen upon one of these bad boys and your life is filled with countless threads, code snippits, and half finished &lt;a href="http://plone.org/documentation/error/poskeyerror/"&gt;reference articles&lt;/a&gt;. Here is a shot at explaining the source of the problem to start with (prevention), and giving a consolidated page of tools to tackle the beast that is POSKeyError head on (treatment).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: large;"&gt;Getting Started&lt;/span&gt;&lt;br /&gt;If you are on this page, there is a good chance you are pooping yourself right now because the database isn't responding, things aren't happy, systems are failing. Let's take this moment to make a copy of everything in its messed up state and while that copies - breathe. Here's your worst case scenario: everything is screwed and you have to restore from backups. I've dealt with several different KeyErrors and never had to restore but that doesn't mean you won't.&lt;br /&gt;&lt;br /&gt;In fact, if you have recent enough backups to where you can restore with minimal data loss, do it. This is not a fast process (set aside at least 3 hours, eons more if you don't have giant cojones). You will not likely fix the source of the problem but at least your dbs will be back online. If you have any scheduled cron jobs for packing the databases or taking backups make sure they are disabled since there is a good chance that one of those could re-trigger the error.&lt;br /&gt;&lt;br /&gt;So start copying and if your db is huge go tell someone to start pulling that restore from tape. Breathe. &lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: large;"&gt;Types of Key Errors&lt;/span&gt;&lt;br /&gt;In my experience, there are two types of key errors - POSKeyError and [connection] KeyError. It's hard to pinpoint which one you have sometimes because they can mask as one another but once you get into debugging you'll know right away.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://spamsch.blogspot.com/2009/04/how-to-repair-broken-zodb-poskeyerror.html"&gt;In theory&lt;/a&gt;, &lt;b&gt;POSKeyError&lt;/b&gt; is short for POSitional Key Error. It's likely to happen if the database gets corrupted, you blow out the file system storage space, your iscsi drive flip out, etc... It &lt;i&gt;may&lt;/i&gt; happen when you cross reference database (more on that later) but I've only seen a more traditional KeyError in that case. Your trackback probably looks something like this*:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;Traceback (most recent call last):&lt;br /&gt;&amp;nbsp; File "/usr/local/lib/python/ZEO/zrpc/connection.py", line 421, in handle_request&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; ret = meth(*args)&lt;br /&gt;&amp;nbsp; File "/usr/local/lib/python/ZODB/FileStorage/FileStorage.py", line 625, in modifiedInVersion&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; pos = self._lookup_pos(oid)&lt;br /&gt;&amp;nbsp; File "/usr/local/lib/python/ZODB/FileStorage/FileStorage.py", line 514, in _lookup_pos&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; raise POSKeyError(oid)&lt;br /&gt;POSKeyError: 0x172024&amp;nbsp;&lt;/pre&gt;&lt;br /&gt;Notice that this is referencing the object that its looking up and the error is not happening in a connection handling phase. This type of error may or may not cause your whole site to fart, depending on where the corrupted object is.&amp;nbsp; For example, if the error is in your portal_skins/custom folder then you are screwed since aquisition is probably picking it up for every site.&lt;br /&gt;&lt;br /&gt;A quick digression on the 0x172024 "thing". This is the oid (object id, in the zodb) of the object that is the offender. For this to be useful in any database manipulation, we will most certainly need to convert it using the p64 function in ZODB.utils. A quick docstring excerpt of two very useful functions we'll use later to convert this oid to an 8-byte string*:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;def p64(v):&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; """Pack an integer or long into a 8-byte string"""&lt;/pre&gt;&lt;pre&gt;def u64(v):&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; """Unpack an 8-byte string into a 64-bit long integer."""&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The other &lt;b&gt;KeyError&lt;/b&gt; (which manifests as POSKeyError and just KeyError, but I'll refer to as just KeyError for clarity) usually has some sort of connection traceback info in the error message like so:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;2009-12-01 16:51:18,056 ERROR @/opt/Zope-2.9/lib/python/ZODB/Connection.py/Connection.py Connection 737&amp;nbsp; : Couldn't load state for 0x01&lt;br /&gt;Traceback (most recent call last):&lt;br /&gt;&amp;nbsp;File "/opt/Zope-2.9/lib/python/ZODB/Connection.py", line 732, in setstate&lt;br /&gt;&amp;nbsp;&amp;nbsp; self._setstate(obj)&lt;br /&gt;&amp;nbsp;File "/opt/Zope-2.9/lib/python/ZODB/Connection.py", line 786, in _setstate&lt;br /&gt;&amp;nbsp;&amp;nbsp; self._reader.setGhostState(obj, p)&lt;br /&gt;&amp;nbsp;File "/opt/Zope-2.9/lib/python/ZODB/serialize.py", line 604, in setGhostState&lt;br /&gt;&amp;nbsp;&amp;nbsp; state = self.getState(pickle)&lt;br /&gt;&amp;nbsp;File "/opt/Zope-2.9/lib/python/ZODB/serialize.py", line 597, in getState&lt;br /&gt;&amp;nbsp;&amp;nbsp; return unpickler.load()&lt;br /&gt;&amp;nbsp;File "/opt/Zope-2.9/lib/python/ZODB/serialize.py", line 479, in _persistent_load&lt;br /&gt;&amp;nbsp;&amp;nbsp; return self.loaders[reference_type](self, *args)&lt;br /&gt;&amp;nbsp;File "/opt/Zope-2.9/lib/python/ZODB/serialize.py", line 540, in load_multi_oid&lt;br /&gt;&amp;nbsp;&amp;nbsp; conn = self._conn.get_connection(database_name)&lt;br /&gt;&amp;nbsp;File "/opt/Zope-2.9/lib/python/ZODB/Connection.py", line 305, in &lt;b&gt;get_connection&lt;br /&gt;&lt;/b&gt;&amp;nbsp;&amp;nbsp; new_con = self._db.databases[database_name].open(&lt;br /&gt;KeyError: 'mysite'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Ewe. This ones a little more mucky. It may or may not have an oid. These &lt;a href="http://plone.org/support/forums/general#nabble-td3658431"&gt;connection KeyErrors&lt;/a&gt; happen when you have multiple database mounts and one database is &lt;a href="http://docs.zope.org/zope3/Code/persistent/wref/WeakRef.1"&gt;weak referencing&lt;/a&gt; another. !@#$ -&amp;gt; what the hell does that mean, right? Let's say you have two Plone Sites, foo and bar, both mounted onto your main db. You are messing around one day and you customize logo.jpg for foo. But you messed up and that was actually meant to be for bar so being a good lazy dev you just use plone's sweet cut and paste functionality to move logo.jpg from foo into bar. Everything is working perfect (and this is indeed "legal"). This will continue to work, until the fateful day that you pack foo and then BAMN! It's key error time.&lt;br /&gt;&lt;br /&gt;When you cut and pasted logo.jpg from foo to bar, you didn't actually create a new object.&amp;nbsp; You created a weak reference to foo from bar.&amp;nbsp; As long as that reference wasn't garbage collected in foo, you were golden. When you &lt;a href="http://old.nabble.com/Missing-loader-for-multidatabase-refs--tt9744503.html#a9744503"&gt;run pack&lt;/a&gt;, it destroys references to the object. The official explanation from ZODB/cross-database-references.txt&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Garbage collection is done on a database by database basis.&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; If an object on a database only has references to it from other&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; databases, then the object will be garbage collected when its&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; database is packed.&amp;nbsp; The cross-database references to it will be&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; broken.&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;What is bar to do now? It can't find the logo.jpg object, and since every page in your site has a logo you are screwed.&lt;br /&gt;&lt;br /&gt;I've seen this happen for non cut/paste reasons as well. If you get the error after a fresh restart on one zope and the others are running fine, then something happened in the mounting of your databases that messed up the stream. I don't know the exact details of why, but I've seen rapid restarts of a zope instance invoke this hella error. Do not restart the other zopes if you can avoid it and use those to start salvaging data just in case.&amp;nbsp; If you are lucky, just go into the undo at the root of the site and undo the last couple of transactions, especially if you see a suspicious addMount transaction. That might be all you need!&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: large;"&gt;Verifying&lt;/span&gt;&lt;br /&gt;Let's get to fixing then shall we! First things first, if you know you had some kind of activity on the disk that may have corrupted the data, we can easily verify by running &lt;a href="http://wiki.zope.org/ZODB/FileStorageBackup"&gt;fstest.py&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;nbsp;/usr/local/bin/fstest.py /srv/zeo/zeo0/Data.fs&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;If nothing is corrupted, it will return nothing. Bad data will return something like&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;/srv/zeo/zeo0/Data.fs truncated possibly because of damaged records at 1670660850&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Yuck! But at least we know what's going on. If this is indeed the case, you may want to start with &lt;a href="http://spamsch.blogspot.com/2009/04/how-to-repair-broken-zodb-poskeyerror.html"&gt;@spamsch's great tutorial&lt;/a&gt; on dealing with transaction corruption and then come back here if that doesn't work.&lt;br /&gt;&lt;br /&gt;To check for bad references, we can&amp;nbsp; use &lt;a href="http://wiki.zope.org/ZODB/FileStorageBackup"&gt;fsrefs.py&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;nbsp;/usr/local/bin/fsrefs.py /srv/zeo/zeo0/Data.fs&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Unfortunately I lost my example of what a bad ref error looks like but its similar to fstest. Most importantly it will tell you the oid of the item that is causing things to break. If you're error message has no oid in it, you will absolutely need this!&lt;br /&gt;&lt;br /&gt;I have not tried it, but it may be worth checking out &lt;a href="http://pypi.python.org/pypi/zc.zodbdgc/0.4.0"&gt;zc.zodbgc&lt;/a&gt; and its multi-zodb-check-refs script. &lt;br /&gt;&lt;br /&gt;If you are certain its a [connection] key error and nothing shows up, you can try to &lt;a href="http://www.zopelabs.com/cookbook/1054240694"&gt;manually search&lt;/a&gt; for it from within the [zopectl] debug console:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;from ZODB.POSException import POSKeyError&lt;br /&gt;def error_finder(folder, exception=POSKeyError, stop_on_first=None):&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; """ start at the given folderish object.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; If stop_on_first is true, quit after one exception;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; otherwise, keep going through the whole tree."""&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; for id, next_item in folder.objectItems():&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; try:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; print id&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; next_item.getId()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; except exception:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; print `exception`, "in folder",&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; print&amp;nbsp; '/'.join(folder.getPhysicalpath()),&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; print "at id:", id&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if stop_on_first:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; raise "done"&amp;nbsp;&amp;nbsp; # hack to break out of recursion&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; else:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; # no error, recurse if it's objectManagerish&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if hasattr(next_item.aq_base, 'objectItems'):&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; error_finder(next_item, exception, stop_on_first)&lt;br /&gt;&lt;br /&gt;error_finder(app.mysite, stop_on_first=True)&lt;br /&gt;&lt;/pre&gt;&lt;span style="font-size: large;"&gt;Fix 'Er Up&lt;/span&gt;&lt;br /&gt;The goal here is simple: delete the offending object. This can be tricky since its hard to delete something that doesn't exist. This can all done from the [zopectl] debug console. If you think this is a connection key error and you are working from zopectl debug, first and foremost modify your config file to get rid of any unnecessary mounts. Broken objects are better than key errors and if it allows you to recover certain parts then even better.&lt;br /&gt;&lt;br /&gt;Once in the debug prompt we can &lt;a href="http://www.zopezone.com/discussions/general/00000168"&gt;find out which object&lt;/a&gt; that oid represents:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;from ZODB.utils import p64&lt;br /&gt;o = root._p_jar[p64(0x172024)&lt;br /&gt;print o.getId()&lt;br /&gt;print o.bobobase_modification_time()&lt;br /&gt;print o.meta_type&lt;br /&gt;print o.__ac_local_roles__&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Hopefully you now know the id of the offending object and lets try to delete it&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;from AccessControl.SecurityManagement import newSecurityManager &lt;br /&gt;admin = app.acl_users.getUserById('admin') &lt;br /&gt;admin = admin.__of__(app.acl_users)&lt;br /&gt;newSecurityManager(None, admin) &lt;br /&gt;from Testing import makerequest&lt;br /&gt;req=makerequest.makerequest(app.mysite.bad_folder)&lt;br /&gt;req.manage_delObjects(['bad_folder'])&lt;br /&gt;import transaction&lt;br /&gt;transaction.commit()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;If that worked and your stuff now loads - lucky you. If all you got was another pos key error, try to delete the parent. If the deletion strategy doesn't work, the next strategy is to fill the void of that object with a fake one, and then delete it. For example:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;app.mysite._setOb('bad_folder', newFolderInstance)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;If neither of the above work, you are probably looking at a connection Key Error. Feel free to try &lt;a href="http://www.mail-archive.com/zodb-dev@zope.org/msg02175.html"&gt;what is described here&lt;/a&gt;, but I have no idea what that code looks like so I won't mention it. Instead let's leave the zope environment and go directly to &lt;a href="http://www.zope.org/Documentation/Articles/ZODB1"&gt;manipulating the ZODB&lt;/a&gt; by hand.&lt;br /&gt;&lt;br /&gt;In the python shell, we can create a connection to the database and manually inspect the objects. We can try manually delete there, and go back in time transaction by transaction. You are probably going to have to poke (use dir!) around so I'm just going to give some getting started code.&amp;nbsp; To manually open the db:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;from ZODB import FileStorage, DB&lt;br /&gt;from ZODB.utils import u64&lt;br /&gt;storage = FileStorage.FileStorage('Data.fs')&lt;/pre&gt;&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre&gt;# don't use this code if you don't have to, especially&lt;/pre&gt;&lt;pre&gt;# if you are having a connection error. You'll know because&lt;/pre&gt;&lt;pre&gt;# app won't let you list its objects without throwing&lt;/pre&gt;&lt;pre&gt;# the db_connection error &lt;br /&gt;&lt;/pre&gt;&lt;pre&gt;db = DB(storage)&lt;br /&gt;connection = db.open()&lt;br /&gt;root = connection.root()&lt;br /&gt;app = root['Application']&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To view the contents of 'app', you must use list to get your keys in sanely readable form:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;list(app.keys()) # returns child nodes of the app&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;If at any point listing the keys fails, you are getting warmer to finding the broken object. Try to delete things if possible, remembering to commit the transaction. Sometimes thats enough to get by. Also note that since you are no longer in a zope environment, all of the objects will be listed as "broken". Don't get obsessed with this - its just because you don't have the proper modules loaded. These are not your key error items and don't get obsessed with fixing them unless you need to traverse in.&lt;br /&gt;&lt;br /&gt;Another route to try is undoing transactions one at a time. Since its &lt;a href="http://docs.zope.org/zodb/zodbguide/transactions.html#undoing-changes"&gt;perfectly explained in the docs&lt;/a&gt;, I won't replicate it but this can do wonders. Remember to commit frequently and take the time to check all the time.&lt;br /&gt;&lt;br /&gt;If you are trying to find a lost object, take advantage of the iterators in zodb. For example, to iterate through the pickle of each object:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;it = storage.iterator()&lt;br /&gt;found = False&lt;br /&gt;while not found:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; try:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; item&amp;nbsp; = it.next().next()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; data = item.data&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; poo = "%s"%data&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if poo.count('broken_object_id'):&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; print "OID: ", u64(item.oid)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; print "TID: ", u64(item.tid)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; maybeThisIsWhatIWant = root._p_jar[item.oid]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; print maybeThisIsWhatIWant&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;If your db screwed the pooch and lost a references, maybe you can use this id to map it back. At this point the options are endless and you will have to be creative so use a copy, take a risk, and jump in to coding with ZODB. Remember: the goal is to delete or restore the reference. &lt;br /&gt;&lt;br /&gt;If something looks incorrect, there is a chance it is since this material exists only in the bermuda triangle so please leave a comment and I'll make sure to update or clarify.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: large;"&gt;Prevention&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: small;"&gt;With multi-reference&lt;/span&gt;, the best thing to do is to NOT cut and paste between mounts. I know its easier said than done. Just remember, copy-paste-delete. In a &lt;a href="http://groups.google.com/group/plonechix/browse_thread/thread/217c71fe849301b4#"&gt;followup discussion&lt;/a&gt;, @davisagli mentioned that in ZODB 3.9 you can disallow cross-references by setting the allow-implicit-cross-referenced. If you're looking into mounting and you have a newer version of plone I would enable that by default.&lt;br /&gt;&lt;br /&gt;[UPDATE] I have reason to believe that the key error from mounting can come from a multi zope setup where the config files are not lined up with each other. I've been able to recreate this on my local server where 2 zopes share zodbs, including the main db, but where one zope has another mount configured. Going to the manage screen and viewing the mount form in each instance show different results but there is also a transaction that occurs in the background, which you can see in the undo log. After just listing the mount points in each instance, the error triggers on the other instance. The simple fix to this is to undo the last transaction, which is labeled "addMountForm". I think anyways...&lt;br /&gt;&lt;br /&gt;Happy Hunting Ploners!&lt;span style="font-size: large;"&gt;&lt;span style="font-size: small;"&gt; &lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: large;"&gt;&lt;span style="font-size: small;"&gt;&amp;nbsp;&lt;/span&gt; &lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: large;"&gt;Other Useful Links&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://david.wglick.org/2009/recombining-zodb-storages/"&gt;Recombining ZODB Storages&lt;/a&gt; &lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;* No DBs were harmed in the generation of these traces. They are 100% real and, yes, you are allowed to feel empathetic to my pain of having to deal with all of them. These are just a few of my favorites. Some day I'll put together a "best-of" compilation, under the influence of course.&lt;br /&gt;* I don't think this applies to Python 3 because it handles unicode differently. I think @davisagli mentioned that but I can't recall at the moment why.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3585841838339962909-1572153881473346491?l=plonechix.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plonechix.blogspot.com/feeds/1572153881473346491/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plonechix.blogspot.com/2009/12/definitive-guide-to-poskeyerror.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3585841838339962909/posts/default/1572153881473346491'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3585841838339962909/posts/default/1572153881473346491'/><link rel='alternate' type='text/html' href='http://plonechix.blogspot.com/2009/12/definitive-guide-to-poskeyerror.html' title='The Definitive Guide to POSKeyError'/><author><name>eleddy</name><uri>http://www.blogger.com/profile/12829844320869028742</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_3RIl2f8RMJQ/R_ZvRpo1IJI/AAAAAAAAB3Y/8t5B8YVPTFc/S220/DSC00141.JPG.jpg'/></author><thr:total>3</thr:total></entry></feed>
