UC Davis College of Agricultural & Environmental Sciences
Personal tools

Web

Sep 16, 2011

SVN to Git

by Trish Ang — last modified Sep 16, 2011 11:40 AM
Filed Under:

Converting a subversion repository over to Github!

It's been awhile!! I'll keep this post short, because in fact there are a ton of other guides on how to do this out there. But I'll put the highlights here and a few notes that I wish had been explained further.

So what's this for anyway? The old Plone UC Davis skin had been hosted in a Subversion repository at https://svn.cse.ucdavis.edu/trac/UCDPloneSkin (requires CAS authentication) which was fine and dandy, but someone brought up that it might be better served at Github, so that it'd be public and others can contribute to it. Having little experience with Github and even less with svn, I thought this would be nigh impossible. Turns out I was wrong! Check it out —

  1. Credit where it's due

    In preparation of the move, you need to properly map the usernames of anyone who had commit code to the old svn to their accounts on Github. Luckily, someone made a handy script to figure out the list for you:
    svn log --quiet http://www.foo.com/path/to/svn/ | grep '^r' | awk -F\| '{print $2}' | sort -u > committers.txt
    That'll output all the svn accounts (email addresses, in this case) to a text file. Then, just add the associated Github accounts in the following format:
    svn account = real name or account name <github account email address>
    i.e. pgang@ucdavis.edu = feesh <feeshsauce@gmail.com>
    or: mrman@ucdavis.edu = Mr. Man <idonthaveagitaccount@gmail.com>
    It took me a few tries to get this right, because I assumed Github would recognize just the account name. Not so! It needs whatever email address they used to sign up for Github to associate it with the correct account.
  2. Bring in the code!

    Next up, you copy everything from that svn repository to your disk or wherever.
    git svn clone <svn repo url> --no-metadata -A committers.txt -t tags -b branches -T trunk <destination dir name>
    I'm not sure what all those add ons are, but the -A pulls the account mapping file you just made and makes the switch for you, I think.
  3. Clean up on aisle three?

    In order to resolve some formatting differences between git and svn, you need to manually convert some branches into tags. When you go into the new .git directory you just made running that git svn clone above, you can run
    git branch -r
    and see all the branches you have. Some of them are called tags/blahblah — these are the ones that need to be switched over. So for each of those, run the following:
    git tag blahblah tags/blahblah
    git branch -r -d tags/blahblah
    Which I think makes the new tag, and then deletes the old branch.
  4. To the cloud!

    Once that's all ready, you can push the whole thing up to Github. This is just the normal, register the repository bit, i.e.
    git remote add origin git@github.com:userid/project.git
    git push origin master --tags
    I think the last --tags part is the only difference, maybe.

Anyway, that's all! The repository is good to go from Github — https://github.com/ucdavis/plone.ucdploneskin — Mostly straightforward, neh? If anything looks incorrect over there, I'd appreciate it if you let me know!

Jun 23, 2011

pkg_add failing to get or fetch a package

by Tyler Randles — last modified Jun 23, 2011 02:10 PM

On our FreeBSD servers pkg_add was failing every time and this is how we fixed it

Trish's latest blog post all about awesome skinning and git was amazing; however she was having problems with pkg_add -r git, FreeBSD 7.1 was looking fore new packages in a non existent directory on freebsd.org.

when she ran the command

pgk_add -r git
  • pkg_add - this is the command to add a package
  • -r - the flag to check the interweb for the package if there is no local copy
  • git - the name of the package we want to add

the output would be

# pkg_add -r git
Error: FTP Unable to get ftp://ftp.freebsd.org/pub/FreeBSD/ports/amd64/packages-7.2-release/Latest/git.tbz: File unavailable (e.g., file not found, no access)
pkg_add: unable to fetch 'ftp://ftp.freebsd.org/pub/FreeBSD/ports/amd64/packages-7.2-release/Latest/git.tbz' by URL
#

The url is wrong and it should be checking in ftp://ftp.freebsd.org/pub/FreeBSD/ports/amd64/packages-7-stable/Latest/. We need to set the value of the system's package site to the correct url. Here is the command to do just that:

setenv PACKAGESITE ftp://ftp.freebsd.org/pub/FreeBSD/ports/amd64/packages-7-stable/Latest/

That is all folks! I hope this helps!

Jun 01, 2011

git & filesystem magic!

by Trish Ang — last modified Jun 01, 2011 01:40 AM

Okay, thanks to one awesome lady, Chrissy Wainwright, I have a new development theme running on our devel server, from git. Let's see how it all went down!

Note: I'll try and contain myself, because I still feel like this is magic and am supes excited that it just WORKS!!! Sure I ran into a few stumbling blocks on the way (see slides 20+), but right now it's amazing: I edit a ploneCustom.css file in a text editor on my Mac, do some git actions, then fire up the page on the development server (not on my Mac)... there be the changes!! <3

Another note: This is largely based off everything I learned from Chrissy Wainwright, über themer from Six Feet Up, and her Plone Theming class last last week at the Penn State Plone Symposium. If you want to start working with this stuff fo' real, I highly recommend learning all the details from her.

git your git in order!

So to preface, we're starting to use github for work as our version control repository. With the theme located on git, both Tyler & I can edit files from our local machines independently.

To start using git, you need to

  1. have it installed on your machine (instructions for FreeBSD == pkg_add -r git)
  2. set up your github account
  3. establish a secure connection between your machine and the github account using SSH keys

Ingredients

Minimum initial structure looks something like: one local machine, one development server, both git enabled; github account with one new repository ready to go (more on this next).

Preparing your theme on server #1

There are a bunch of resources like this one on how to start a new theme on the filesystem, so I'll just start with the basics and you can read more about this elsewhere. Note, Plone themes you're developing should go in the src folder.

Theme From scratch!

On our development server (running Plone 3.3.5 on FreeBSD), in develserver/Plone/zinstance/src I created a new theme using paster by running:

paster create -t plone3_theme awesome.theme

...and then going through the prompts accordingly. (More about each prompt on this guide.) Another note, if you're running Plone 4 and it asks you to enter the skinbase, Plone Default means the classic Plone 3 look (light blue and white) and Sunburst Theme is the new Plone 4 default with the large boxes as navigation.

Next up, Buildout

Now you need to get your instance to recognize your new theme. Do this by referencing the new awesome.theme in your buildout.cfg as follows:

eggs =
   ...
   awesome.theme

zcml =
   ...
   awesome.theme

develop =
   ...
   src/awesome.theme

The ... just mean there are probably other entries there in your buildout as well.

Run buildout, cross your fingers, restart your instance, and with any luck in your ZMI the new theme product will show up!

Moving along to the ZMI...

In your ZMI, create a new Plone site on which to play with this theme. If all went well with the previous buildout step, once you go to portal_quickinstaller, you should see your Awesome Theme in the list of available products! Choose it and click install, and you should be good to go.

Note: if it doesn't show up there... Well... I had that problem for awhile, but I can't say what exactly fixed it. Make sure it looks to be added correctly in your buildout.cfg for starters, and then I tried a round of clearing the cache, killing varnish, restarting varnish, restarting the instance, rerunning buildout, restarting the whole damn server, until it finally just magically worked... Good luck!!

Once it's installed and running correctly, try making a simple change from your file system to ensure that it's working. For example, in src/awesome.theme/awesome/theme/skins/awesome_theme_styles you can create ploneCustom.css and add something like body {background: #FF0000;}. In theory, you should be able to refresh your page and see the change! (Once again, hit a snag here, but make sure your portal_css is in debug mode, and if necessary restart your instance).

Back to git

Excellent! Now to get this onto Github, here's what you do... navigate back into src/awesome.theme for starters.

In your web browser, log back into http://github.com and in the lower right you should see an area for Your Repositories. Click the New Repository button to the right of this. Make the Project Name the same as your theme, i.e. awesome.theme and hit Create.

Github will give you instructions on how to initialize your folder for use with the repository. Follow 'em! I ran into one problem here, but it turned out I had mistyped and set the origin incorrectly the first time, and it wouldn't make any commits after that. If that happens to you, here's a handy guide on removing remotes, and then just run through the original instructions again, but in general you should be fine just copying and pasting the commands out of their guide thing.

Once you're done initializing the folder, add everything into git by doing the following:

git add *
git commit -a
git push

Getting the theme on your local machine

Alrighty then! Now you should have a shiny new theme ready to develop on the development server, and synced to the Github repository you created. In theory, you already have Plone 3.3.5 also running on your local machine. I don't exactly... but that's a whole other matter that I can't deal with right now... The following should work regardless:

On your local machine's file system, navigate to yourmachine/Plone/zinstance/src and create a new folder awesome.theme — once inside, run the following command:

git clone git@github.com:yourUsername/awesome.theme.git

You'll copy the end part (git@github.com:stuff) from, in your browser on your repository page on Github, the box next to where it says Read+Write access. This will pull a copy of the theme onto your machine!

Enter buildout, part deux

Now to get it recognized by the Plone instance on your machine, enter awesome.theme into the buildout.cfg in the same way as above, into eggs, zcml, and develop (src/awesome.theme). Rerun your buildout and do all the fancy restarting that you need to, fire up your Plone instance, and check to see if you can install Awesome Theme on a new Plone site!

End game

If all is successful, then you now have the theme pulled from git running successfully on two Plone instances. Huzzah! In theory how this will work is, we'll no longer have to touch anything directly on the development server. I can make a bunch of changes to my local machine and at various stages, check things into git, log onto the development server, do a git pull and see the changes live there! Here's how this might look:

On the local machine

  1. Edit src/awesome.theme/awesome/theme/skins/ploneCustom.css with something like body {font-size: 10em; color: #00FF00;} /* hulk smash!!! */
  2. In your terminal, in the src/awesome.theme folder
    git commit -a  // it'll open a file, write a comment and then save
    git push

On the Development Server

  1. In your src/awesome.theme folder, do a git pull
  2. Reload your Plone site. Et voila..?!

If that doesn't work, once again check to make sure the cache is cleared, things are properly restarted, portal_css is in debug mode, etc. Hopefully you'll get to the point where that's unnecessary and changes show up just after running through those steps above (make change, commit, push, pull, refresh). Chrissy also showed us a handy add-on, plone.reload, for refreshing your Plone instance's ZCML and code without having to restart the entire instance.

Questions? Better methods? I'd love to hear it! Although our comments are currently disabled... *shakes fist* must install Plone recaptcha soon...

May 25, 2011

Lessons from the past few days

by Trish Ang — last modified May 25, 2011 02:10 AM

Buildout hates me. I have to accept it. It's just not meant to be.

  1. Running buildout fifty times a day does not equate to productivity.
  2. Version pinning is your friend! More importantly...
  3. First: Products.ContentWellPortlets has been updated to a version dependent on Plone 4.0. So for us lowly Plone 3.3.5 runners, pin it to 2.1 (Products.ContentWellPortlets==2.1)
  4. Second: plone.reload is also unpinned, but it needs to be set to 1.5 for Plone 3.3.5 as well (plone.reload==1.5)
  5. UnknownExtra: zope.i18n 0.0 has no such extra feature 'zcml' — is actually code for "Aww haaail no, do I look like Plone 4!? I'm Plone 3.3.5, so quit trying to use those fancy new add-ons with me."
  6. Even after those lovely discoveries, buildout is still the devil, and refuses to compile successfully.

Very frustrating. Tyler & I got back from Plone Symposium East this weekend, and I have so many new things I want to try & implement!! Why must you hinder me so, buildout? Anyway, PSE11 deserves a write-up of its own. In fact, the Plone Theming training I took from Six Feet Up also warrants an entirely other write-up, with everything Chrissy taught! Amazing! Even that last blog entry about event collection portlets — blew it out of the water with a simple portal catalog query and repeating tal statement. I'll divulge later though, maybe after buildout quits snackin on hater tots...

In the meantime, check out SteveM's awesome and thorough sprint report! Rapture sprint FTW!

Apr 19, 2011

Collection of events

by Trish Ang — last modified Apr 19, 2011 11:10 AM

Call me crazy, but sometimes I like using collection portlets for events (i.e. events of a certain category) or event-type-clones.

This doesn't always bode well though: by default collection portlets show the last modified date of an item, not the event start date as you may be expecting. Luckily, it's an easy fix, since we have a model in the events portlet. Here's the plan:

The code

Really all it is is a tal statement to display the object start date. The tal:condition part of it checks to make sure the item has a start date, before trying to display it. I like keeping it in a span to give it a class for styling purposes.

<span class="portletEventDate" tal:condition="obj/start" tal:content="python:toLocalizedTime(obj.start)">
   May 5
</span>

Another thought is you could just swap the portletItemDetails date (currently obj.Date) to just be the start date (obj.start) but this doesn't fly if you need to use collection portlets elsewhere on your site for non-events.

By the way, this all goes into the collection.pt template which can be found in portal_view_customizations. I put it the line after <span class="portletItemDetails"...> ... </span> but you can put it wherever you feel is useful. Enjoy!

p.s. We've been getting a crap ton of spam. Like, gran turismo cheats and all sorts of diet pills and propecia fo' life. Until I can get around to putting PloneCaptcha or something on here, comments are on lockdown!

Feb 22, 2011

plone is a battlefield

by Trish Ang — last modified Feb 22, 2011 01:11 AM

Recap from the West Coast Plone Cioppino Sprint, a.k.a lessons learned over a counter of mutilated crab carcasses.

Tyler at Bodega

If Plone is a battlefield, then I daresay we came out victorious last weekend, rallied under a glorious Plone-blue banner and standing triumphant over a pile of hella closed tickets & brutalized crustaceans. Not sure if that analogy works quite right, but unified and kick-ass are still appropriate descriptors for the results of the sprint!

Just shy of a dozen Plone-developing folk planted in a beautiful house overlooking Bodega Bay — fully-stocked with alcohol (damn Ross Patterson's endless supply Manhattans were delicious), gourmet food (thank you, head chef Steve McMahon), and a hot tub — produce amazing results. The goals of the sprint focused on achievable wins for the whole Plone community, on items like Plone Software Center and Plone.net. To be honest, the week prior and the entire car ride up, Tyler & I were asking ourselves Why are we going? and What the hell are two designers going to work on for three days? We thought, Well, maybe Plone.net needs a bump up from the couch upholstery-like theme it was currently rocking... So off we went.

Turns out (lucky for us?), there are tickets a'plenty for Plone-workers of all kinds! Here's an overview of the main issues I remember us working on:

  • Plone.org login form

    (First off, let me just say, never in my life did I expect to be editing something on Plone.org — and yet! I guess that's just what people do at Plone sprints! Ridiculous.) 

    This problem was a quick CSS fix. The input fields on the login form ran directly next to the text labels, but with Tyler's favorite display: inline-block attribute and a width of something or other, it was all good. The trickiest part here was figuring out how to actually commit the change. This is still a mystery to me, so we handed it off to SteveM to commit it for us. :)

  • Plone.org printing issues

    Two dilemmas here! First one started in a ticket — someone noticed that the print format of Plone.org included the right column and footer unnecessarily, and suggested some CSS to leave those out. Tyler & I agreed that it caused at least an entire extra (often blank) page to print, so we used their CSS.

    However, on discussing this further, removing the footer and/or sitemap at the bottom of the page is debatable — is it necessary? Who makes those decisions? Do we just make the change in the interest of the greater good and hope that flies with the Plone deities out there..?

    Anyway, the second thing we found was that Firefox wasn't printing more than one page of anything at all. Preposterous! After a bunch of detective work, we figured out which stylesheet was the culprit, and thanks to Tyler's über research abilities, discovered it was a known float issue in Firefox and entirely fixable. I can't remember the exact CSS, I think we just had to put float: none on something, but it prints properly now. Huzzah!

  • Plone.org Support

    The biggest thing we worked on was implementing a new template for the Plone.org Support section, in preparation for the shift to StackOverflow for documentation. Limi had a mock-up ready and a general idea of how things should work, then Tyler & I were given free reign to mess around in Plone.org's ZMI. 

    Having never skinned anything in Plone 4 before, we took a bit to figure out what was different with the new columnized framework, the grid-row grid-cell biznass. To be honest, we never quite figured out how to get the doc_view to place the content in the right area, so we hacked it with CSS. May God have mercy on our souls. 

    Other work included making new icons and adding the new StackOverflow logo, and otherwise just styling the page. Here's the final product! (Well, it looks off right now there, someone's been working on it still I think, but here's what it looked like when we left Bodega Bay...)

    support-v2.png

    In retrospect, given that zenwryly successfully imported the Plone.net content into the Support section of Plone.org, that menu is going to need to change to accommodate it now, no?

  • Plone.org Sprints

    Lastly, the Sprints page was in need of some clean-up. We turned the page into a Collection, moved all the stray Sprint event items into one folder, and set the collection to display the most recent three events (sprints). Simple enough!

Moral of the story?

on top of Goat Rock

The real wins here, though, were not that we helped out with a little CSS action or that I learned of the amazing combination that is rosemary crackers, fig jam, & brie (thank you, zenwryly!). No no, I think what we really came away with can be summed up with the following bulleted list:

  • Solidarity: Buildout makes everyone cry, not just us.
  • Location: Tyler and I are infinitely more productive working from a couch and from the hours of 3pm to 2am. (Hint: Adam, please requisition us a couch for our cubicle, posthaste! Also, relocate Mrak Hall to Bodega Bay, kthx!) :)
  • Open source ftw: Anyone really can help out in an open source community! And sometimes you do need that diversity of contributors.
  • Community: It's been said before and it still holds true, the Plone community rocks. And is fueled by alcohol.
  • And because five seems more complete than four: Killing crabs is awesome. Octobush. Hot tub heated documentation&theming debates at 3am. Hell yes, #cioppinosprint, let's do this again soon.

 PS: Did I mention Goat Rock is amazing & eleddy is a kick-ass top rope / rappelling instructor? ERNEST? Yep!

Feb 21, 2011

plone 4 migration test-run!

by Trish Ang — last modified Feb 21, 2011 11:00 PM

You can ignore this, I'm just gonna putz around my personal server and see if I can't upgrade a few 3.1.7 websites to plone 4.0.2... (Spoiler alert: migration FAIL.)

Who wakes up on the Monday of a three-day weekend and says, my look how sunny it is outside! I think today is a great day to upgrade my Plone server! Well apparently, it happens. Here're the deets: I haz one personal server hosted by Webfaction (they're awesome by the way, always quick & thorough with their customer service) that houses a few extracurricular or volunteer websites. They've been on Plone 3.1.7 since I first set up the account.

First thoughts — where the hell do I start? Luckily a Google search turned up one article from Webfaction's support docs that suggests just starting up a new Plone 4 instance, copying over the data.fs from the old Plone 3 instance, and then just doing the migrations on there. Sure, sounds good. Here's what I did:

  1. On Webfaction's control panel, creating a new instance is as easy as clicking a few buttons. Went to Domains/Websites > Applications. Hit the Add new button in the bottom right, and choose App Category: Zope, then App: Zope 2.12.13 - Plone 4.0.2. Give your instance a name, hit Create, set it, aaand forget it! A few minutes later you're golden.
  2. Once it's all good, create a Domain for it under Domains/Websites > Domain. Then go to Websites under Domains/Websites > Websites and create a new one for your new instance. Select the Subdomain you just made from the list, and in Site apps choose the name of your new instance. Enter a / for the URL Path.
  3. To copy the data.fs over, ssh in to your server and do the following: 
    1. Stop the Plone 4 instance. Go to /webapps/yourPlone4instance/zinstance/bin and do ./instance stop
    2. Go to your data.fs, it's in /webapps/yourPlone4/instance/zinstance/var/filestorage. I think you can just empty this directory, but since I'm paranoid I just renamed all the existing data.fs.whatevs files there to data.fs.whatevs-old.
    3. Stop your Plone 3 instance. Do something like /webapps/yourPlone3instance/zinstance/bin/instance stop
    4. Copy the data.fs over from there to here, so cp /webapps/yourPlone3instance/zinstance/var/filestorage/data.fs . <-- that last dot means "here" 
    5. Start up both instances again with /webapps/theinstancename/zinstance/bin/instance start
  4. Good! Now get into the ZMI of your new Plone 4 server (http://whateveryournewdomainis.com/manage). Once you click into any of your sites, you'll be prompted with a nice big warning that, oh shit you've got an old version of Plone here! Click the Continue with upgrade link there and proceed to step 5. 
    1. But wait, what if you click into a site and get an error? I hit this on one of my sites:
      Error Type: AttributeError
      Error Value: type object 'ICalendarSupport' has no attribute '__iro__'
    2. ...a nice reminder that I had add-on products installed and completely forgot about 'em! Much like Macaulay Culkin in Home Alone, they're less than pleased with the situation.
    3. Okay so from here, I went back and copied in the eggs I needed from my old buildout.cfg to my new buildout.cfg (and the zcml's as necessary!). Ran buildout, seemingly ended well, started up the instance but no dice! 
      Error: error opening file /home/blah/webapps/zope2/zinstance/parts/instance/etc/zope.conf: [Errno 2] No such file or directory: '/home/blah/webapps/zope2/zinstance/parts/instance/etc/zope.conf'
      Missing zope.conf? Turns out this just means buildout didn't run as successfully as I'd hoped. So looking back...
    4. Oh man, one error:
      The version, 1.1.2, is not consistent with the requirement, 'plone.app.jquerytools>=1.2dev'.
      While:
        Installing instance.
      Error: Bad version 1.1.2

      Fffffuuuuu....
    5. Right. Googling that, I found that maybe Products.PloneFormGen is requiring a newer version of jquerytools, but that pinning an older version (<=1.7dev) should work. (Here and here.) Side note, because I didn't know what pinning an older version meant: apparently you can just put Products.PloneFormGen<=1.7dev in the egg section of buildout.cfg! Fancy that. Run buildout, start the instance, and...
      1. Okay new dilemma. The instance starts fine! The offending project from above (AttributeError) now opens correctly and can proceed to the next step. Some of my other sites, however, are not so lucky. The new error, when clicking on the name in the ZMI, is:
        ('Could not adapt', , )

        What's that? Adapt what, now? And then the error that shows up when trying to access the page directly is, oddly:
        NotFound('
        Site Error
        \n
        An error was encountered while publishing this resource.\n
        \n
        Resource not found
        \n\n Sorry, the requested resource does not exist.
        Check the URL and try again.
        Resource: http://new.apiq.webfactional.com/blah/front-page/document_view
        \n 

        ... Okay I think I need to throw in the towel for today. Accessing a different website gives me this lovely super long error, and Google has not been my friend for the past hour (and to be honest I left and got Korean food, because I thought that would help :P). Ah well, it was so promising! I suppose when I pick this back up, I'll try uninstalling the products I don't need on the Plone 3 instance and then re-importing the Data.fs. Til next time.
  5. (In theory, this is where I'll pick up when hitting the @@plone-upgrade works).

 

Feb 08, 2011

An average day in the life of your web designer

by Tyler Randles — last modified Feb 08, 2011 11:35 AM
Filed Under:

following us through an average day on the job in CRU

Trish and Tyler
just your run-of-the-mill Plone sweatshop

7:00 - 7:30: Wake up, shower, drink coffee

7:30 - 7:45: Commute to work, buy more coffee on the way

7:45 - 8:00: Circle for parking, walk into work, eyes adjust to lack of natural light

8:00 - 8:17: Log in, wait for security patch to be deployed, check email,  join #Plone

8:17 - 10:57: Patch seven web servers

10:57 - 11:15: Resolve and close web tickets

11:15 - ????: Consume more coffee, go to a meeting, create more websites, ask Tom for help, break Plone, watch buildout run, ask Tom for more help, fix Plone, eat lunch, answer phone calls, watch buildout run, pin 37 product versions, watch buildout run again, break FreeBSD, respond to emails, restart varnish, train users, discover bug in outdated version of Plone, create more websites, start pound, pound head on desk, have another meeting, send follow up email, delete log files fill up all remaining hard drive space, drink coffee, and finally go home

Jan 18, 2011

Kupu not saving changes in Plone2.5

by Tyler Randles — last modified Jan 18, 2011 08:50 AM
Filed Under:

Users can not save changes when using Plone 2.5, Kupu 1.3.9 and Firefox 3.6

Since we are fortunate enough to still be running several Plone 2.5 sites (we are trying to change this!) when our users received the latest version of Firefox they were unable to save edits to the website.  The problem was only Firefox, Internet Explorer, Safari and Chrome all worked.  Thankfully it was not the recent move from OpenBSD to FreeBSD but a problem with sarissa.js as found by https://bugs.launchpad.net/silva/+bug/512236.  To fix our sites in the zmi i did a search for sarissa.js and added:

try {
               XMLDocument.prototype.readyState = 0;
           } catch(e) {
               // XXX on some browsers (Firefox 3.6 at least) this fails,
               // however I think this is rarely a problem so we just
               // ignore it here...
           };

after:

             * <li>3 == INTERACTIVE,</li>
             * <li>4 == COMPLETED</li></ul>
             */

and removed 

           XMLDocument.prototype.readyState = 0;

in the end sarissa.js looks like:

/*****************************************************************************
 *
 * Sarissa XML library version 0.9.6
 * Copyright (c) 2003 Manos Batsis, 
 * mailto: mbatsis at users full stop sourceforge full stop net
 * This software is distributed under the Kupu License. See
 * LICENSE.txt for license text. See the Sarissa homepage at
 * http://sarissa.sourceforge.net for more information.
 *
 *****************************************************************************

 * ====================================================================
 * About
 * ====================================================================
 * Sarissa cross browser XML library 
 * @version 0.9.6
 * @author: Manos Batsis, mailto: mbatsis at users full stop sourceforge full stop net
 *
 * Sarissa is an ECMAScript library acting as a cross-browser wrapper for native XML APIs.
 * The library supports Gecko based browsers like Mozilla and Firefox,
 * Internet Explorer (5.5+ with MSXML3.0+) and, last but not least, KHTML based browsers like
 * Konqueror and Safari.
 *
 */
/**
 * <p>Sarissa is a utility class. Provides static methods for DOMDocument and 
 * XMLHTTP objects, DOM Node serializatrion to XML strings and other goodies.</p>
 * @constructor
 */
function Sarissa(){};
/** @private */
Sarissa.PARSED_OK = "Document contains no parsing errors";
/**
 * Tells you whether transformNode and transformNodeToObject are available. This functionality
 * is contained in sarissa_ieemu_xslt.js and is deprecated. If you want to control XSLT transformations
 * use the XSLTProcessor
 * @deprecated
 * @type boolean
 */
Sarissa.IS_ENABLED_TRANSFORM_NODE = false;
/**
 * tells you whether XMLHttpRequest (or equivalent) is available
 * @type boolean
 */
Sarissa.IS_ENABLED_XMLHTTP = false;
/**
 * tells you whether selectNodes/selectSingleNode is available
 * @type boolean
 */
Sarissa.IS_ENABLED_SELECT_NODES = false;
var _sarissa_iNsCounter = 0;
var _SARISSA_IEPREFIX4XSLPARAM = "";
var _SARISSA_HAS_DOM_IMPLEMENTATION = document.implementation && true;
var _SARISSA_HAS_DOM_CREATE_DOCUMENT = _SARISSA_HAS_DOM_IMPLEMENTATION && document.implementation.createDocument;
var _SARISSA_HAS_DOM_FEATURE = _SARISSA_HAS_DOM_IMPLEMENTATION && document.implementation.hasFeature;
var _SARISSA_IS_MOZ = _SARISSA_HAS_DOM_CREATE_DOCUMENT && _SARISSA_HAS_DOM_FEATURE;
var _SARISSA_IS_SAFARI = navigator.userAgent.toLowerCase().indexOf("applewebkit") != -1;
var _SARISSA_IS_IE = document.all && window.ActiveXObject && navigator.userAgent.toLowerCase().indexOf("msie") > -1  && navigator.userAgent.toLowerCase().indexOf("opera") == -1;

if(!window.Node || !window.Node.ELEMENT_NODE){
    var Node = {ELEMENT_NODE: 1, ATTRIBUTE_NODE: 2, TEXT_NODE: 3, CDATA_SECTION_NODE: 4, ENTITY_REFERENCE_NODE: 5,  ENTITY_NODE: 6, PROCESSING_INSTRUCTION_NODE: 7, COMMENT_NODE: 8, DOCUMENT_NODE: 9, DOCUMENT_TYPE_NODE: 10, DOCUMENT_FRAGMENT_NODE: 11, NOTATION_NODE: 12};
};

// IE initialization
if(_SARISSA_IS_IE){
    // for XSLT parameter names, prefix needed by IE
    _SARISSA_IEPREFIX4XSLPARAM = "xsl:";
    // used to store the most recent ProgID available out of the above
    var _SARISSA_DOM_PROGID = "";
    var _SARISSA_XMLHTTP_PROGID = "";
    var _SARISSA_THREADEDDOM_PROGID = "";
    var _SARISSA_XSLTEMPLATE_PROGID = "";
    /**
     * Called when the Sarissa_xx.js file is parsed, to pick most recent
     * ProgIDs for IE, then gets destroyed.
     * @param idList an array of MSXML PROGIDs from which the most recent will be picked for a given object
     * @param enabledList an array of arrays where each array has two items; the index of the PROGID for which a certain feature is enabled
     */
    Sarissa.pickRecentProgID = function (idList, enabledList){
        // found progID flag
        var bFound = false;
        for(var i=0; i < idList.length && !bFound; i++){
            try{
                var oDoc = new ActiveXObject(idList[i]);
                o2Store = idList[i];
                bFound = true;
                for(var j=0;j<enabledList.length;j++)
                    if(i <= enabledList[j][1])
                        Sarissa["IS_ENABLED_"+enabledList[j][0]] = true;
            }catch (objException){
                // trap; try next progID
            };
        };
        if (!bFound)
            throw "Could not retreive a valid progID of Class: " + idList[idList.length-1]+". (original exception: "+e+")";
        idList = null;
        return o2Store;
    };
    //============================================
    // Factory methods (IE)
    //============================================
    // see non-IE version
    Sarissa.getDomDocument = function(sUri, sName){
        if (!_SARISSA_DOM_PROGID) {
            _SARISSA_DOM_PROGID = Sarissa.pickRecentProgID(["Msxml2.DOMDocument.4.0", "Msxml2.DOMDocument.3.0", "MSXML2.DOMDocument", "MSXML.DOMDocument", "Microsoft.XMLDOM"], [["SELECT_NODES", 2],["TRANSFORM_NODE", 2]]);
        };
        var oDoc = new ActiveXObject(_SARISSA_DOM_PROGID);
        // if a root tag name was provided, we need to load it in the DOM
        // object
        if (sName){
            // if needed, create an artifical namespace prefix the way Moz
            // does
            if (sUri){
                oDoc.loadXML("<a" + _sarissa_iNsCounter + ":" + sName + " xmlns:a" + _sarissa_iNsCounter + "=\"" + sUri + "\" />");
                // don't use the same prefix again
                ++_sarissa_iNsCounter;
            }
            else
                oDoc.loadXML("<" + sName + "/>");
        };
        return oDoc;
    };
    // see non-IE version   
    Sarissa.getParseErrorText = function (oDoc) {
        var parseErrorText = Sarissa.PARSED_OK;
        if(oDoc.parseError != 0){
            parseErrorText = "XML Parsing Error: " + oDoc.parseError.reason + 
                "\nLocation: " + oDoc.parseError.url + 
                "\nLine Number " + oDoc.parseError.line + ", Column " + 
                oDoc.parseError.linepos + 
                ":\n" + oDoc.parseError.srcText +
                "\n";
            for(var i = 0;  i < oDoc.parseError.linepos;i++){
                parseErrorText += "-";
            };
            parseErrorText +=  "^\n";
        };
        return parseErrorText;
    };
    // see non-IE version
    Sarissa.setXpathNamespaces = function(oDoc, sNsSet) {
        oDoc.setProperty("SelectionLanguage", "XPath");
        oDoc.setProperty("SelectionNamespaces", sNsSet);
    };   
    /**
     * Basic implementation of Mozilla's XSLTProcessor for IE. 
     * Reuses the same XSLT stylesheet for multiple transforms
     * @constructor
     */
    XSLTProcessor = function(){
        if (!_SARISSA_XSLTEMPLATE_PROGID) {
            _SARISSA_XSLTEMPLATE_PROGID = Sarissa.pickRecentProgID(["Msxml2.XSLTemplate.4.0", "MSXML2.XSLTemplate.3.0"], [["XSLTPROC", 2]]);
        };
        this.template = new ActiveXObject(_SARISSA_XSLTEMPLATE_PROGID);
        this.processor = null;
    };
    /**
     * Impoprts the given XSLT DOM and compiles it to a reusable transform
     * @argument xslDoc The XSLT DOMDocument to import
     */
    XSLTProcessor.prototype.importStylesheet = function(xslDoc){
        // convert stylesheet to free threaded
        if (!_SARISSA_THREADEDDOM_PROGID) {
            _SARISSA_THREADEDDOM_PROGID = Sarissa.pickRecentProgID(["MSXML2.FreeThreadedDOMDocument.4.0", "MSXML2.FreeThreadedDOMDocument.3.0"]);
        }
        var converted = new ActiveXObject(_SARISSA_THREADEDDOM_PROGID); 
        converted.loadXML(xslDoc.xml);
        this.template.stylesheet = converted;
        this.processor = this.template.createProcessor();
        // (re)set default param values
        this.paramsSet = new Array();
    };
    /**
     * Transform the given XML DOM
     * @argument sourceDoc The XML DOMDocument to transform
     * @return The transformation result as a DOM Document
     */
    XSLTProcessor.prototype.transformToDocument = function(sourceDoc){
        this.processor.input = sourceDoc;
        var outDoc = new ActiveXObject(_SARISSA_DOM_PROGID);
        this.processor.output = outDoc; 
        this.processor.transform();
        return outDoc;
    };
    /**
     * Not sure if this works in IE. Maybe this will allow non-well-formed
     * transformation results (i.e. with no single root element)
     * @argument sourceDoc The XML DOMDocument to transform
     * @return The transformation result as a DOM Fragment
     */
    XSLTProcessor.prototype.transformToFragment = function(sourceDoc, ownerDocument){
        return this.transformToDocument(sourceDoc);
    };
    /**
     * Set global XSLT parameter of the imported stylesheet
     * @argument nsURI The parameter namespace URI
     * @argument name The parameter base name
     * @argument value The new parameter value
     */
    XSLTProcessor.prototype.setParameter = function(nsURI, name, value){
        /* nsURI is optional but cannot be null */
        if(nsURI){
            this.processor.addParameter(name, value, nsURI);
        }else{
            this.processor.addParameter(name, value);
        };
        /* update updated params for getParameter */
        if(!this.paramsSet[""+nsURI]){
            this.paramsSet[""+nsURI] = new Array();
        };
        this.paramsSet[""+nsURI][name] = value;
    };
    /**
     * Gets a parameter if previously set by setParameter. Returns null
     * otherwise
     * @argument name The parameter base name
     * @argument value The new parameter value
     * @return The parameter value if reviously set by setParameter, null otherwise
     */
    XSLTProcessor.prototype.getParameter = function(nsURI, name){
        if(this.paramsSet[""+nsURI] && this.paramsSet[""+nsURI][name])
            return this.paramsSet[""+nsURI][name];
        else
            return null;
    };
}
else{ /* end IE initialization, try to deal with real browsers now ;-) */
   if(_SARISSA_HAS_DOM_CREATE_DOCUMENT){
        if(window.XMLDocument){
            /**
            * <p>Emulate IE's onreadystatechange attribute</p>
            */
            XMLDocument.prototype.onreadystatechange = null;
            /**
            * <p>Emulates IE's readyState property, which always gives an integer from 0 to 4:</p>
            * <ul><li>1 == LOADING,</li>
            * <li>2 == LOADED,</li>
            * <li>3 == INTERACTIVE,</li>
            * <li>4 == COMPLETED</li></ul>
            */


try {
                XMLDocument.prototype.readyState = 0;
            } catch(e) {
                // XXX on some browsers (Firefox 3.6 at least) this fails,
               // however I think this is rarely a problem so we just
               // ignore it here...
            };


            /**
            * <p>Emulate IE's parseError attribute</p>
            */
            XMLDocument.prototype.parseError = 0;

            // NOTE: setting async to false will only work with documents
            // called over HTTP (meaning a server), not the local file system,
            // unless you are using Moz 1.4+.
            // BTW the try>catch block is for 1.4; I haven't found a way to check if
            // the property is implemented without
            // causing an error and I dont want to use user agent stuff for that...
            var _SARISSA_SYNC_NON_IMPLEMENTED = false;
            try{
                /**
                * <p>Emulates IE's async property for Moz versions prior to 1.4.
                * It controls whether loading of remote XML files works
                * synchronously or asynchronously.</p>
                */
                XMLDocument.prototype.async = true;
                _SARISSA_SYNC_NON_IMPLEMENTED = true;
            }catch(e){/* trap */};
            /**
            * <p>Keeps a handle to the original load() method. Internal use and only
            * if Mozilla version is lower than 1.4</p>
            * @private
            */
            XMLDocument.prototype._sarissa_load = XMLDocument.prototype.load;

            /**
            * <p>Overrides the original load method to provide synchronous loading for
            * Mozilla versions prior to 1.4, using an XMLHttpRequest object (if
            * async is set to false)</p>
            * @returns the DOM Object as it was before the load() call (may be  empty)
            */
            XMLDocument.prototype.load = function(sURI) {
                var oDoc = document.implementation.createDocument("", "", null);
                Sarissa.copyChildNodes(this, oDoc);
                this.parseError = 0;
                Sarissa.__setReadyState__(this, 1);
                try {
                    if(this.async == false && _SARISSA_SYNC_NON_IMPLEMENTED) {
                        var tmp = new XMLHttpRequest();
                        tmp.open("GET", sURI, false);
                        tmp.send(null);
                        Sarissa.__setReadyState__(this, 2);
                        Sarissa.copyChildNodes(tmp.responseXML, this);
                        Sarissa.__setReadyState__(this, 3);
                    }
                    else {
                        this._sarissa_load(sURI);
                    };
                }
                catch (objException) {
                    this.parseError = -1;
                }
                finally {
                    if(this.async == false){
                        Sarissa.__handleLoad__(this);
                    };
                };
                return oDoc;
            };
        };//if(window.XMLDocument)

        /**
         * <p>Ensures the document was loaded correctly, otherwise sets the
         * parseError to -1 to indicate something went wrong. Internal use</p>
         * @private
         */
        Sarissa.__handleLoad__ = function(oDoc){
            if (!oDoc.documentElement || oDoc.documentElement.tagName == "parsererror")
                oDoc.parseError = -1;
            Sarissa.__setReadyState__(oDoc, 4);
        };
        
        /**
        * <p>Attached by an event handler to the load event. Internal use.</p>
        * @private
        */
        _sarissa_XMLDocument_onload = function(){
            Sarissa.__handleLoad__(this);
        };
        
        /**
         * <p>Sets the readyState property of the given DOM Document object.
         * Internal use.</p>
         * @private
         * @argument oDoc the DOM Document object to fire the
         *          readystatechange event
         * @argument iReadyState the number to change the readystate property to
         */
        Sarissa.__setReadyState__ = function(oDoc, iReadyState){
            oDoc.readyState = iReadyState;
            if (oDoc.onreadystatechange != null && typeof oDoc.onreadystatechange == "function")
                oDoc.onreadystatechange();
        };
        /**
        * <p>Factory method to obtain a new DOM Document object</p>
        * @argument sUri the namespace of the root node (if any)
        * @argument sUri the local name of the root node (if any)
        * @returns a new DOM Document
        */
        Sarissa.getDomDocument = function(sUri, sName){
            var oDoc = document.implementation.createDocument(sUri?sUri:"", sName?sName:"", null);
            oDoc.addEventListener("load", _sarissa_XMLDocument_onload, false);
            return oDoc;
        };        
    };//if(_SARISSA_HAS_DOM_CREATE_DOCUMENT)
};
//==========================================
// Common stuff
//==========================================
if(!window.DOMParser){
    /** 
    * DOMParser is a utility class, used to construct DOMDocuments from XML strings
    * @constructor
    */
    DOMParser = function() {
    };
    /** 
    * Construct a new DOM Document from the given XMLstring
    * @param sXml the given XML string
    * @param contentType the content type of the document the given string represents (one of text/xml, application/xml, application/xhtml+xml). 
    * @return a new DOM Document from the given XML string
    */
    DOMParser.prototype.parseFromString = function(sXml, contentType){
        var doc = Sarissa.getDomDocument();
        doc.loadXML(sXml);
        return doc;
    };
    
};

if (!window.XMLHttpRequest && window.ActiveXObject) {
    /**
     * Emulate XMLHttpRequest
     * @constructor
     */
    XMLHttpRequest = function() {
        if(!_SARISSA_XMLHTTP_PROGID){
            _SARISSA_XMLHTTP_PROGID = Sarissa.pickRecentProgID(["Msxml2.XMLHTTP.4.0", "MSXML2.XMLHTTP.3.0", "MSXML2.XMLHTTP", "Microsoft.XMLHTTP"]);
        };
        return new ActiveXObject(_SARISSA_XMLHTTP_PROGID);
    };
}
if(window.XMLHttpRequest){
    Sarissa.IS_ENABLED_XMLHTTP = true;
};

if(!window.document.importNode && _SARISSA_IS_IE){
    try{
        /**
        * Implements importNode for the current window document in IE using innerHTML.
        * Testing showed that DOM was multiple times slower than innerHTML for this,
        * sorry folks. If you encounter trouble (who knows what IE does behind innerHTML)
        * please gimme a call.
        * @param oNode the Node to import
        * @param bChildren whether to include the children of oNode
        * @returns the imported node for further use
        */
        window.document.importNode = function(oNode, bChildren){
            var importNode = document.createElement("div");
            if(bChildren)
                importNode.innerHTML = Sarissa.serialize(oNode);
            else
                importNode.innerHTML = Sarissa.serialize(oNode.cloneNode(false));
            return importNode.firstChild;
        };
        }catch(e){};
};
if(!Sarissa.getParseErrorText){
    /**
     * <p>Returns a human readable description of the parsing error. Usefull
     * for debugging. Tip: append the returned error string in a &lt;pre&gt;
     * element if you want to render it.</p>
     * <p>Many thanks to Christian Stocker for the initial patch.</p>
     * @argument oDoc The target DOM document
     * @returns The parsing error description of the target Document in
     *          human readable form (preformated text)
     */
    Sarissa.getParseErrorText = function (oDoc){
        var parseErrorText = Sarissa.PARSED_OK;
        if(oDoc.parseError != 0){
            /*moz*/
            if(oDoc.documentElement.tagName == "parsererror"){
                parseErrorText = oDoc.documentElement.firstChild.data;
                parseErrorText += "\n" +  oDoc.documentElement.firstChild.nextSibling.firstChild.data;
            }/*konq*/
            else if(oDoc.documentElement.tagName == "html"){
                parseErrorText = Sarissa.getText(oDoc.documentElement.getElementsByTagName("h1")[0], false) + "\n";
                parseErrorText += Sarissa.getText(oDoc.documentElement.getElementsByTagName("body")[0], false) + "\n";
                parseErrorText += Sarissa.getText(oDoc.documentElement.getElementsByTagName("pre")[0], false);
            };
        };
        return parseErrorText;
    };
};
Sarissa.getText = function(oNode, deep){
    var s = "";
    var nodes = oNode.childNodes;
    for(var i=0; i < nodes.length; i++){
        var node = nodes[i];
        var nodeType = node.nodeType;
        if(nodeType == Node.TEXT_NODE || nodeType == Node.CDATA_SECTION_NODE){
            s += node.data;
        }
        else if(deep == true
                    && (nodeType == Node.ELEMENT_NODE
                        || nodeType == Node.DOCUMENT_NODE
                        || nodeType == Node.DOCUMENT_FRAGMENT_NODE)){
            s += Sarissa.getText(node, true);
        };
    };
    return s;
};
if(window.XMLSerializer){
    /**
     * <p>Factory method to obtain the serialization of a DOM Node</p>
     * @returns the serialized Node as an XML string
     */
    Sarissa.serialize = function(oDoc){
        return (new XMLSerializer()).serializeToString(oDoc);
    };
}else{
    if((Sarissa.getDomDocument("","foo", null)).xml){
        // see non-IE version
        Sarissa.serialize = function(oDoc) {
            // TODO: check for HTML document and return innerHTML instead
            return oDoc.xml;
        };
        /**
         * Utility class to serialize DOM Node objects to XML strings
         * @constructor
         */
        XMLSerializer = function(){};
        /**
         * Serialize the given DOM Node to an XML string
         * @param oNode the DOM Node to serialize
         */
        XMLSerializer.prototype.serializeToString = function(oNode) {
            return oNode.xml;
        };
    };
};

/**
 * strips tags from a markup string
 */
Sarissa.stripTags = function (s) {
    return s.replace(/<[^>]+>/g,"");
};
/**
 * <p>Deletes all child nodes of the given node</p>
 * @argument oNode the Node to empty
 */
Sarissa.clearChildNodes = function(oNode) {
    while(oNode.hasChildNodes()){
        oNode.removeChild(oNode.firstChild);
    };
};
/**
 * <p> Copies the childNodes of nodeFrom to nodeTo</p>
 * <p> <b>Note:</b> The second object's original content is deleted before 
 * the copy operation, unless you supply a true third parameter</p>
 * @argument nodeFrom the Node to copy the childNodes from
 * @argument nodeTo the Node to copy the childNodes to
 * @argument bPreserveExisting whether to preserve the original content of nodeTo, default is false
 */
Sarissa.copyChildNodes = function(nodeFrom, nodeTo, bPreserveExisting) {
    if(!bPreserveExisting){
        Sarissa.clearChildNodes(nodeTo);
    };
    var ownerDoc = nodeTo.nodeType == Node.DOCUMENT_NODE ? nodeTo : nodeTo.ownerDocument;
    var nodes = nodeFrom.childNodes;
    if(ownerDoc.importNode) {
        for(var i=0;i < nodes.length;i++) {
            nodeTo.appendChild(ownerDoc.importNode(nodes[i], true));
        };
    }
    else{
        for(var i=0;i < nodes.length;i++) {
            nodeTo.appendChild(nodes[i].cloneNode(true));
        };
    };
};

/**
 * <p> Moves the childNodes of nodeFrom to nodeTo</p>
 * <p> <b>Note:</b> The second object's original content is deleted before 
 * the move operation, unless you supply a true third parameter</p>
 * @argument nodeFrom the Node to copy the childNodes from
 * @argument nodeTo the Node to copy the childNodes to
 * @argument bPreserveExisting whether to preserve the original content of nodeTo, default is false
 */
Sarissa.moveChildNodes = function(nodeFrom, nodeTo, bPreserveExisting) {
    if(!bPreserveExisting){
        Sarissa.clearChildNodes(nodeTo);
    };
    
    var nodes = nodeFrom.childNodes;
    // if within the same doc, just move, else copy and delete
    if(nodeFrom.ownerDocument == nodeTo.ownerDocument){
        nodeTo.appendChild(nodes[i]);
    }else{
        var ownerDoc = nodeTo.nodeType == Node.DOCUMENT_NODE ? nodeTo : nodeTo.ownerDocument;
         if(ownerDoc.importNode && (!_SARISSA_IS_IE)) {
            for(var i=0;i < nodes.length;i++) {
                nodeTo.appendChild(ownerDoc.importNode(nodes[i], true));
            };
        }
        else{
            for(var i=0;i < nodes.length;i++) {
                nodeTo.appendChild(nodes[i].cloneNode(true));
            };
        };
        Sarissa.clearChildNodes(nodeFrom);
    };
    
};

/** 
 * <p>Serialize any object to an XML string. All properties are serialized using the property name
 * as the XML element name. Array elements are rendered as <code>array-item</code> elements, 
 * using their index/key as the value of the <code>key</code> attribute.</p>
 * @argument anyObject the object to serialize
 * @argument objectName a name for that object
 * @return the XML serializationj of the given object as a string
 */
Sarissa.xmlize = function(anyObject, objectName, indentSpace){
    indentSpace = indentSpace?indentSpace:'';
    var s = indentSpace  + '<' + objectName + '>';
    var isLeaf = false;
    if(!(anyObject instanceof Object) || anyObject instanceof Number || anyObject instanceof String 
        || anyObject instanceof Boolean || anyObject instanceof Date){
        s += Sarissa.escape(""+anyObject);
        isLeaf = true;
    }else{
        s += "\n";
        var itemKey = '';
        var isArrayItem = anyObject instanceof Array;
        for(var name in anyObject){
            s += Sarissa.xmlize(anyObject[name], (isArrayItem?"array-item key=\""+name+"\"":name), indentSpace + "   ");
        };
        s += indentSpace;
    };
    return s += (objectName.indexOf(' ')!=-1?"</array-item>\n":"</" + objectName + ">\n");
};

/** 
 * Escape the given string chacters that correspond to the five predefined XML entities
 * @param sXml the string to escape
 */
Sarissa.escape = function(sXml){
    return sXml.replace(/&/g, "&amp;")
        .replace(/</g, "&lt;")
        .replace(/>/g, "&gt;")
        .replace(/"/g, "&quot;")
        .replace(/'/g, "&apos;");
};

/** 
 * Unescape the given string. This turns the occurences of the predefined XML 
 * entities to become the characters they represent correspond to the five predefined XML entities
 * @param sXml the string to unescape
 */
Sarissa.unescape = function(sXml){
    return sXml.replace(/&apos;/g,"'")
        .replace(/&quot;/g,"\"")
        .replace(/&gt;/g,">")
        .replace(/&lt;/g,"<")
        .replace(/&amp;/g,"&");
};
//   EOF

 

 

Jan 13, 2011

Moving servers and the lessons we learned

by Tyler Randles — last modified Jan 13, 2011 01:40 PM

We have one final web server running on the "bare" hardware, the rest are hyper-v VMs and for months we have wanted to visualize it.  Several weeks ago I outlined a plan for how to do this, tested it and then had Trish test it.  Each time we discovered another step, fine tuned our troubleshooting skills, and work out all of Plone and Zope quirks.  Armed with our proven step by step guide we decided that the change over was going to happen this Wednesday 1/12/11 at 3:00PM and this was our plan:

  1. Double check and repeat migration instructions
  2. Email the hostclerk (they run the schools DNS entries)
  3. Email every web site editor for the effected sites
  4. Post the outage on computing.caes.ucdavis.edu
  5. Coordinate with our friends at the Held Desk / Infrastructure who keep us up and running
  6. Blocked off 24 hours in case anything goes wrong

We forgot to add one thing on the list:Plone is not dependable or consistent

  1. permissions, zeoserver just did not want to stay up and running. This was in the error logs
    2011-01-03T02:10:13 INFO ZEO.runzeo (43359) created PID file '/usr/local/Plone/zeocluster/server/var/ZEO.pid'
    ------
    2011-01-03T02:10:13 INFO ZEO.runzeo (43359) opening storage '1' using FileStorage
    ------
    2011-01-03T02:10:13 INFO ZEO.runzeo (43359) removed PID file '/usr/local/Plone/zeocluster/server/var/ZEO.pid'
    ------
    2011-01-03T02:10:13 INFO root sleep 8 to avoid rapid restarts
    ------
    2011-01-03T02:10:13 INFO root pid 43359: exit status 1
    ------
    2011-01-03T02:10:21 INFO root spawned process pid=43360
    (To see this error message i used tail /usr/local/Plone/zeocluster/client1/log/Z2.log)
    In the end it was a BSD permissions issue fixed by a chown -R plone:plone * when in the /usr/local/Plone folder
  2. Cleanup: In the same directory as our Data.fs lives several other files (namely Data.fs.index, Data.fs.lock, Data.fs.temp) all of which needed to be deleted before Plone and Zope can start
  3. To fast!: With all of our practice and clear off the old sites we actually "completed" (see bellow) the migration in half the time we had expected it to take. Ordinarily this does not sound like a problem except in our case the hostclerk was not going to update the DNS entries to point to our new site until the following day and we could not request it to be done that day because it was after 5:00. Trish came to the rescue and disabled the /login_form on the old server so we could bring up both without fearing our users could edit the old site. 
  4. Kupu: Today after congratulating ourselves on a speedy job well done we received an email stating that our users could not edit the body text of the website.  After some frantic googling, checking multiple platforms, every plone site, redoing the java script registries, cleaning all of the caches and still no luck, we fixed the problem by reinstalling kupu from the zmi in portal_quickinstaller for each site

In the end it was not too painful, we are no longer dependent on old hardware and we learned about Plone along the way

Fall Quarter Review

by Alex Suh — last modified Jan 13, 2011 12:31 PM

A short blog of my first quarter interning at CRU

Although it was a blur, my first quarter as an intern at CRU has ended. In that quarter my head was jam-packed full of plone, internet comics, random internet phenomena, ridiculous episodes of fox shows, and some less fun and tedious activities (eye-bleeding excel spreadsheets) . I'm not sure how much I like the entire basement office aspect of it, but I had great fun working here. I got to explore new aspects of design, as well as develop and sharpen my graphic design skills more. Hopefully, I did not slow down Trish and Tyler too much last quarter. I was also able to learn more about clients and the people side of design work in general, and how often times, they have no idea what they want. I really enjoyed my first experience as an intern and hope to keep learning even more.

Jan 05, 2011

West Coast Plone Cioppino sprint!

by Trish Ang — last modified Jan 05, 2011 08:53 AM
Filed Under:

West coast represent. Looks like there will be a Plone sprint coming up early this February

Check out their Coactivate page for more information and to sign up.

Date: February 9-13 (Wed-Sun) 2011
Place: Bodega Bay, CA

Topics: This will be a sprint focused on a limited number of pre-defined topics, possibly to include:

  • Improving Plone's approachability for evaluators
  • Overhauling and modernizing Plone.net/PloneServicesCenter
  • Overhauling and modernizing PloneHelpCenter
  • Documentation
  • Other topics TBD: but we want to keep the focus on achievable tasks that provide immediate wins for Plone-the-product or Plone-the-community.

Dec 15, 2010

jQueried background-image slideshow

by Trish Ang — last modified Dec 15, 2010 04:08 PM

This jQuery solution from Marcofolio.com mimics the large header image slideshow from http://visitphilly.com

I stumbled across this slideshow code last night while working on a freelance project, but it worked out really smoothly so I wanted to document it here! Plus, this would be a good one to implement as an alternative to some of our current slideshows.

I won't detail all the code because the original tutorial over here goes into all that. The part that was interesting to me was how it was set up to gracefully fade into the next image. Essentially, there are two divs, headerimg1 and headerimg2.

    <div id="headerimgs">
        <div id="headerimg1" class="headerimg"></div>
        <div id="headerimg2" class="headerimg"></div>
    </div>

The CSS puts them absolutely positioned one on top of the other.

.headerimg { background-position: left top; background-repeat: no-repeat; width:832px; height:372px; position:absolute;}

The images are identified in an array at the top of the .js file. Then, the code assigns an image as the background for each of the divs, and dynamically cycles through the images every couple seconds. It also simultaneously makes one div display: block and decrements its z-index, while making the other display: none. That way, it's sitting behind the top image ready to show up when the other fades out.

        // Make sure the new container is always on the background
        currentZindex--;
        
        // Set the background image of the new active container
        $("#headerimg" + activeContainer).css({
            "background-image" : "url(images/" + photoObject.image + ")",
            "display" : "block",
            "z-index" : currentZindex
        });

        // Fade out the current container
        // and display the header text when animation is complete
        $("#headerimg" + currentContainer).fadeOut(function() {
            setTimeout(function() {
                $("#headertxt").css({"display" : "block"});
                animating = false;
            }, 500);
        });

Pretty clever!

(Which reminds me, Tyler found this the other day: Google jQueery and pick the top link ♥)

Nov 29, 2010

And we have a winner!

by Tyler Randles — last modified Nov 29, 2010 01:45 PM

Our unit needed a sign for the door and instead of using Microsoft Word to create it, we had a one hour design sprint to create the perfect sign (or as close to prefect as possible).

We were asked by our helpdesk infrastructure team to create a sign for our office door when they are out of the office helping users.  Instead of firing up the good old fashion word processor and cranking it out in Calibri, we went back to our roots in print design and challenged ourselves. In the interest time we decided to do this as a sprint and this is how we did it:

  1. The copy we had to use was
    Welcome to the Computing Resources Unit!
    This door is closed for security reasons when all helpdesk staff members are busy assisting users.
    If you require technical support, please try the following:
    -          Check the student assistant helpdesk in the 150 Mrak Hall office
    -          Send an email to our helpdesk at csrequests@caes.ucdavis.edu<mailto:csrequests@caes.ucdavis.edu>
    -          Call our helpdesk at 530-754-7122
    -          Submit a help request on the web at http://computing.caes.ucdavis.edu/help
    
  2. we had one hour
  3. one black and white design
  4. one color design
  5. one design with an image
  6. one UGLY design
  7. must fit on an 8.5 x 11 piece of paper

After the hour we meet, held a critique and decided on:

CRU_DOORPOSTER.png

We all had a lot of fun and plan on doing it again! Here are a few of our others designs:

CRU_door_sign_02.jpg CRU_door_sign_05.jpg CRU_door_sign_04.jpg CRU_door_sign_01.jpg CRU_door_sign_03.jpg

Nov 19, 2010

It was a little too quiet around here....

by Tyler Randles — last modified Nov 19, 2010 03:20 PM

Trish and I have been doing less blogging recently and more documentation for our knowledge base. I wanted to take a quick break and explain how we organized and how it has helped!

To the two people out there who read my blog posts (Trish and my mother) you may have noticed that I have not been posting recently, my deepest and sincerest apologies.  I still do use Plone everyday but I have been working on our documentation instead of blogging.  I figured this would be a great opportunity to explain the "kb" folder on our site and how we decided to structure our content.

As usual we have some words of warning, storing Plone documentation in Plone is dangerous, if your Plone instillation breaks will losse access to  your documentation (you will have to hope google has an index of it and you can look at the google cache of the page)

The structure of our kb folder looks something like this:

KB
 |-generic
     |-products
        |-faculty staff directory
     |-training
 |-caes
     |-how to edit the splash image
 |-ffhi
     |-faculty staff directory

In the knowledge base there are two types of information: specific and general.  All general information (how to use Plone, how to add a staf member, and how to use collective.uploadify) is documented in the generic category and all screen shots use a generic Plone 3 installation.  The second category, specific information, we organize by website/center.  In this category we list specific customizations or how to's that do not apply to other sites, for example, we have documented how to change the splash image on the college site in the CAES folder. You may have noticed faculty staff directory is listed twice above, this is because the general how to is listed under generic > products but the specific information about how to update it on the FFHI website is listed under FFHI

The blog is aimed at the general Plone Community and the knowledge base is aimed at our users.  In the knowledge base there are protected folders and pages, I am sorry if you encounter any of these but not all of the information there is meant for the general public. If you have any questions we are happy to help, just ask! 

 

 

Nov 16, 2010

Collector's Item

by Trish Ang — last modified Nov 16, 2010 01:55 PM

Collection portlets are a handy tool — they let you display a certain number of items from a collection in a portlet. We use them for things like specific news item feeds, Faculty Staff Directory profiles, etc. Here's a bit on how to customize them!

First off! If you're not sure what a Collection is, maybe you should read up over here?

Warning! Not to discourage you, reader, but you should know up front that editing the template for the collection portlet affects all of your collection portlets, not surprisingly! What this means is, if on your website you want one that shows event dates only for collections that show events, and thumbnails only on collections that show profiles — the template is the same. Luckily you can CSS your way around most of these issues. I think you can even clone the portlet to make other custom portlets but that's for another day. Anyway, read on!

The Template

So this magic template lives in the ZMI in portal_view_customizations. Go on over and find it at the bottom area, collection.pt. For you lazy folks, here's what the original looks like a la Plone 3.3.5:

<dl class="portlet portletCollection"
    i18n:domain="plone"
    tal:define="collection_url view/collection_url;
                plone_view context/@@plone;
                toLocalizedTime nocall:plone_view/toLocalizedTime;
                getIcon nocall:plone_view/getIcon;">

    <dt class="portletHeader">
        <span class="portletTopLeft"></span>
        <a tal:attributes="href collection_url" tal:omit-tag="not:view/data/show_more">
            <span tal:content="view/data/header" />
        </a>
        <span class="portletTopRight"></span>
    </dt>
    
    <tal:events tal:repeat="obj view/results">
    <dd class="portletItem"
        tal:define="oddrow repeat/obj/odd;
                    item_icon python:getIcon(obj);"
        tal:attributes="class python:oddrow and 'portletItem even' or 'portletItem odd'">
        <a href="#"
           class="tile"
           tal:attributes="href obj/getURL;
                           title obj/Description">
            <img tal:replace="structure item_icon/html_tag" />
            <span tal:replace="obj/pretty_title_or_id">
             Title
            </span>
            <span class="portletItemDetails"
                  tal:condition="view/data/show_dates"
                  tal:content="python:toLocalizedTime(obj.Date)">
                Date
            </span>
        </a>
    </dd>
    </tal:events>
    
    <dd class="portletFooter" tal:condition="view/data/show_more">
        <span class="portletBottomLeft"></span>
        <span>
        <a tal:attributes="href collection_url" i18n:translate="more_url">
             More&hellip;
             </a>
        </span>
        <span class="portletBottomRight"></span>
    </dd>

</dl>

The meat of it (between <tal:events>) is just taking each item from whichever Collection you associate the portlet with, and spitting out its title with a link, associated image if there is one, and the details like its effective date. Here's one way we've used the Collection Portlet:

News items

Why yes, Plone does have its own News Portlet to display News Items. But what if you only want to display News Items tagged with certain categories? That's where Collections come in! 

For our purposes, we have two types of News Items on the front of our College website — Web News and Spotlight articles. We set this up before we knew of the wonder that is ContentWellPortlets, so it's a little hacky but the principle is the same. There are two Collections set up to look for News Items, one that looks for those with the first tag, and one with the second tag. Then we have one Collection portlet in the left column set to show three items, and one Collection portlet in the right column showing only one item. You set which items show up (i.e. the three most recent) in the Collection criteria, but you can have the portlet choose random items as well.

The main customization we did with these was to have the Description field show up as a little blurb, and to make the "More..." link in the footer point to the correct archive. 

For adding the Description or any additional field in, it's just a matter of finding the correct query string and kind of modeling the syntax after an existing line (coming from me, a non-programmer). Here's the code I put in:

<tal:events tal:repeat="obj view/results">
<!-- all the normal stuff, and then... -->
   <p class="documentDescription" tal:content="obj/Description">Blah</p>
</dd>
</tal:events>

The key piece there is the tal statement that is swapping the content of the p tag with the object's description. 

For the "More..." link, we did a hacky kind of thing where there's a <span class="webnews">More...</span> and a <span class="spotlight">More...</span>, and then a .portal-column-one .portletFooter .spotlight, .portal-column-two .portletFooter .webnews {display: none;} to hide them in the other portlet.

That's the general idea though! Let me know if you have questions or are looking to do something more specific. Hooray for Collection Portlets!

Nov 12, 2010

“If it ain’t broke, don’t fix it” But what if I want to upgrade?

by Tyler Randles — last modified Nov 12, 2010 03:50 PM

How we upgrade a Plone 2.5 site/server to Plone 3 while keeping the Plone 2.5 site/server online for a seamless transition

Part 1 the getting ready

We have a Plone 2.5 server that is chugging along like an old truck, it requires little to no maintenance and is extremely dependable.  However, now that Plone 4 is out we are feeling a bit self-conscious about our old truck. So the time has come, we will be upgrading our Plone 2.5 sites to Plone 3.x.
For this upgrade we will need three Plone servers:

  • Our original Plone 2.5 server (server A)
  • A new identical Plone 2.5 server (server B)
  • The new Plone 3 server (server C)

Part 2 preparing the server to be upgraded to Plone 3

  1. Stop Plone on server A
  2. Stop Plone on server B
  3. Rename the data.fs on server B to data_old.fs
  4. On server B use scp to copy the data.fs from server A
    1. cd /usr/local/Plone/zeocluster/server/var/ (or wherever your Data.fs is)
    2. scp username@serverA:/home/zope/zeoserver/var/Data.fs . (note that last period!)
  5. Start Plone on server B and make sure Plone starts up correctly

Part 3 preparing the site(s) to be upgraded to Plone 3

On server B in the zmi for each site:

  1. use the “portal_quickinstaller” uninstall all non-standard Plone products.  For us this is “PloneCASLogin” and “AnalyticsForPlone.”
  2. in portal_skins/custom rename:
    1. main_template and plonecustom.css
    2. to main_template_old and plonecustom_old.css

Part 4 moving the site(s) to a Plone 3 server

There are two ways to move a site to server C.  The first way is the more Plone.org approved: moving the data.fs. The second way is the shunned and specifically outlawed way: moving the zexp

Moving the entire server with the Data.fs

  1. Stop Plone on server B
  2. Stop Plone on server C
  3. Rename the data.fs on server C to data_old.fs
  4. On server C use scp to copy the data.fs from server B
    1. cd /usr/local/Plone/zeocluster/var/filestorage/
    2. scp username@serverA:/home/zope/zeoserver/var/Data.fs
  5. Start Plone on server C and make sure Plone starts up correctly

Moving one site with the zexp

  1. Log into server B's ZMI directly (bypassing all load balancers, for us this is http://serverB:8080/manage)
  2. Check the check box next to the Plone site and click "import/export" at the bottom
  3. Click "export" the defaults are:
    1. Export object id: the site name
    2. Export to: save to server
    3. XML format?: unchecked
  4. When zope is done exporting the website the page will redirect to the ZMI and display a message at the top saying "The site has been successfully exported to /usr/local/Plone/zeocluster/client1/var/WebSite.zexp"
  5. SSH into server C
  6. Copy the .zexp from server B to server C for both client1 and client2 using scp
    scp zope@serverB:/usr/local/Plone/zeocluster/client1/var/WebSite.zexp /usr/local/Plone/zeocluster/parts/client1/import
    scp zope@serverB:/usr/local/Plone/zeocluster/client1/var/WebSite.zexp /usr/local/Plone/zeocluster/parts/client2/import

  7. Log into server C's ZMI directly (bypassing all load balancers, for us this is http://serverC:8080/manage)
  8. Click "import/export" at the bottom of the ZMI
  9. On the lower half of the screen, in the "import" section, click the drop down and select "WebSite.zexp"
  10. Click import and wait, when zope is done importingthe website the page will redirect to the ZMI and display a message at the top saying "The WebSite has been successfully imported!"

Part 5 upgrading the specific Plone site

  1. Log into server C's ZMI directly (bypassing all load balancers, for us this is http://serverC:8080/manage)
  2. Click on the Plone SIte you want to upgrade, WebSite, click portal_migration there should be a red ! next to it
  3. Check "Dry Run Mode" (to make sure the upgrade will work without permanently modifying it) and click "upgrade"
  4. When you get the message "Migration has succeeded" go back to portal_migration and click upgrade
  5. Once the upgrade has succeeded load up your site http://serverC/Plone and it should have your content and the default plone skin!

 

Oct 28, 2010

The Joy of Cooking...with XDV

by Trish Ang — last modified Oct 28, 2010 03:10 PM
Filed Under:

Cooking up a beautifully themed Plone site, that is! We gave a presentation on what we learned from the LA Plone Sprint over the summer, so here are our notes from that.

Someday when I'm not caught between back to back meetings I'll write a more pleasant introduction to this material, but in the meantime! Here are the slides from our presentation.

Liz Leddy (@eleddy) wrote a great recap of our whole sprint over here on the PloneChix blog.

At the sprint Tyler and I put together a theme based on Halcyon, a free HTML template by Spyka. Here's a zip file with our rules.xml, template.html, and our ploneCustom.css*, etc. (during the sprint we used styles.css instead, placed it in the portal_skins/custom folder, and registered it in the portal_css registry).

Lastly, the rules.xml file from our talk today looked like:

<rules xmlns="http://namespaces.plone.org/xdv">
    <!-- title -->
    <replace content='/html/head/title'    
               theme='/html/head/title' />               

    <!-- Base tag -->
    <replace theme="/html/head/base" content="/html/head/base" />
    <prepend theme="/html/head" content="/html/head/link | /html/head/style" />
    <prepend theme="/html/head" content="/html/head/script" />    
    <prepend content="/html/body/@class" theme="/html/body" /> 

    <!-- examples: 
         note the syntax but don't worry about the details yet        
 
    <copy content='//*[@id="where-it's-coming-from"]'    
               theme='//*[@id="where-it's-going-to"]' />   -->

    <copy content='//*[@id="portal-column-content"]'    <!-- Plone body into HTML footer -->
               theme='//*[@id="footer"]' />         

    <copy content='//*[@id="portal-logo"]'              <!-- Plone logo into HTML logo -->
               theme='//*[@id="logo"]' />                         
</rules>

 

Oct 27, 2010

Uploadification

by Trish Ang — last modified Oct 27, 2010 02:05 PM

Basic setup instructions for collective.uploadify — a multifileuploader for Plone.

What? You say you're tired of adding new files and images one by one using Plone's 'Add new' interface? Preposterous! There is no more enjoyable task than stepping one by one through that 'Add new' > 'Browse' > 'Save' rinse & repeat process.

...Unless you're any sane human being. Luckily! There's a lovely jQuery-based multiple file uploader add-on called Uploadify. It works as a portal action on an object, so that you navigate to a folder, append @@upload to the address, and then away you go uploading a storm of files all at once!

Basic set up

First install collective.uploadify through your buildout.cfg. Oddly enough, this product doesn't need to be added in through portal_quickinstaller. Instead, to activate it you create a new portal action for it:

  • In your ZMI, navigate to /portal_actions/object. Add a new CMF Object using the drop-down in the top right of the page.
  • In the URL (Expression) field, enter:
    string:${folder_url}/@@upload
    and choose Visible.
  • Back up from the root of the ZMI, go to /portal_properties/site_properties. At the very bottom, add a new property. The name should be ul_size_limit, type is string, and the value is whatever size limit you want there to be for uploads in bytes. We set ours at 15000000.
    There are a ton of extra customizable options available at http://plone.org/products/collective.uploadify

Boom! You're good to go.

Who's got issues?

Apparently we've got issues. On most of the sites we've installed collective.uploadify on, it works like a charm! There's one site in particular though where the Browse button does not show up, thus making it a little difficult to upload anything. Granted, we've had issues with this site before, but this just happens to be a site where they upload hundreds of .pdfs and could really use something like uploadify! Has anyone else run into this problem? I know this person, grahamperrin, has but no one acknowledged their comment in the #plone IRC chat room, unfortunately. Please let us know if you've found a way to fix the issue. 

Uploadify issue
What's the deal? And ignore those carrot orange buttons, please...

The source for the broken example looks like:

<fieldset id="uploadify">
<legend>Upload</legend>

<!-- uploadify comes here -->
<div id="uploader"></div>

<hr />

<!-- upload button -->

<a id="uploadify-upload" class="context"
href="javascript:jq('#uploader').uploadifyUpload();">Upload Files</a>
<!-- clear button -->
<a id="uploadify-clear-queue" class="context"
href="javascript:jq('#uploader').uploadifyClearQueue();">Clear Queue</a>
</fieldset>

While the source for the working example looks like:

<fieldset id="uploadify">
<legend>Upload</legend>

<!-- uploadify comes here -->
<div id="uploader"></div>

<hr />

<div id="uploader" style="display: none;" width="110" height="30"></div>
<object height="30" width="110" type="application/x-shockwave-flash" data="++resource++uploader.swf" id="uploaderUploader" style="visibility: visible;">
<param name="quality" value="high">
<param name="wmode" value="opaque">
<param name="allowScriptAccess" value="sameDomain">
<param name="flashvars" value="uploadifyID=uploader&amp;pagepath=/CAES/&amp;buttonText=BROWSE&amp;script=@@upload_file&amp;folder=/CAES&amp;scriptData=cookie%3D&amp;width=110&amp;height=30&amp;wmode=opaque&amp;method=POST&amp;queueSizeLimit=999&amp;simUploadLimit=4&amp;fileExt=*.*;&amp;multi=true&amp;fileDataName=Filedata">
</object>

<div class="uploadifyQueue" id="uploaderQueue"></div>

<a id="uploadify-upload" class="context"
href="javascript:jq('#uploader').uploadifyUpload();">Upload Files</a>
<!-- clear button -->
<a id="uploadify-clear-queue" class="context"
href="javascript:jq('#uploader').uploadifyClearQueue();">Clear Queue</a>
</fieldset>

I'm wondering if it's something to do with it being a ++ resource thing. The problem we had before on this site had to do with a missing rule in our ++resource++manage-portlets.kss file, which worked after we cleared the cache and reloaded it.

Oct 26, 2010

diving into plone

by Alex Suh — last modified Oct 26, 2010 01:36 PM

Hi, my name is Alex Suh and I am an aspiring web designer and fresh-faced intern, and today was my first experience with plone. I was a little confused at first, but I think I will survive. I have been doing web design for about a year now, and I hope to absorb a lot from Tyler and Trish. Hopefully, my brain does not explode. Anyways, It was awesome meeting everyone!                           -Alex