Deploying your own EtherPad on Ubuntu
There are lots of walkthroughs on setting up your own EtherPad instance running around out there, but many of them either stop after localhost:9000 (hardly useful for a collaborative editing tool) or just don’t quite fit your situation.
I wanted to set up a public instance running at etherpad.jordanhollinger.com running behind an Apache proxy on top of Ubuntu Server 9.04. If you want to do something very much like that, this may be the walk-through for you. Otherwise, keep searching.
Before we begin you should
- be comfortable on the Linux command line
- have some notion of configuring Apache
- realize the EtherPad code base is in a state of flux
- realize this walk-through is intended more for a hobby instance than a fully-secured and enterprise-ready installation
- accept that your milage may vary
Also, note that the material for walk-through was culled from some wikis, several other EtherPad walkthroughs, and the Apache Web Server documentation. If at any point you run into trouble, you may want to peruse some of these source documents:
- code.google.com/p/etherpad/wiki/Instructions
- wiki.ubuntu.com/Etherpad
- karteek.selfdabba.com/post/301488675/etherpad-opensourced
- httpd.apache.org/docs/2.0/mod/mod_proxy.html
Prep your Env
First, open your apt repository list,
sudo vim /etc/apt/sources.list
add
deb http://archive.ubuntu.com/ubuntu hardy universe multiverse
deb-src http://archive.ubuntu.com/ubuntu hardy universe multiverse
deb http://us.archive.ubuntu.com/ubuntu/ hardy-updates universe multiverse
deb-src http://us.archive.ubuntu.com/ubuntu/ hardy-updates universe multiverse
and run sudo apt-get update.
Now that we have access to all the packages we need, we can install them.
sudo apt-get install sun-java6-jdk scala mysql-server mysql-client libmysql-java mercurial apache2
Download the MySQL Java connector from www.mysql.com/downloads/connector/j/ and move it to /usr/share/java/mysql-connector-java.jar.
We’ll need some environmental variables set. You can edit /etc/profile or create something like /etc/profile.d/etherpad.sh. Add the following:
export JAVA_HOME="/usr/lib/jvm/java-6-sun"
export SCALA_HOME="/usr/share/java"
export MYSQL_CONNECTOR_JAR="/usr/share/java/mysql-connector-java.jar"
You’ll need to restart your shell for these to take effect.
Grab the Pad
hg clone https://etherpad.googlecode.com/hg/ etherpad
cd etherpad/trunk/etherpad
Patch the Pad
For the installer to find everything it needs in Ubuntu, you’ll need to edit ../infrastructure/bin/makejar.sh:
-JARFILES=`echo $SCALA_HOME/lib/scala-library.jar lib/*.jar lib/manifest`
+JARFILES=`echo $SCALA_HOME/scala-library.jar lib/*.jar lib/manifest`
- $JAR xf $SCALA_HOME/lib/scala-library.jar
+ $JAR xf $SCALA_HOME/scala-library.jar
At this time, “etherpad.com” is still hard-coded in lots of places under src/. You’ll need to change this to your domain. To get a list of all the files you need to change, run
grep -irl "etherpad.com" src/
One particularly important file to change is src/etherpad/globals.js. Be sure to add your domain to the SUPERDOMAINS hash.
Now bin/setup-mysql-db.sh needs some patching:
Add mysql="mysql" right above db="etherpad".
Add -pMyPassword after -u root in the two mysql calls.
Configure the Pad
Open etc/etherpad.localdev-default.properties and change
- devMode to false
- etherpad.isProduction to true
- listen to localhost:9000
- etherpad.isPNE to true (only if you want to run in PNE mode – I don’t know what that means)
- etherpad.adminPass to something better
Put it in a jar
bin/rebuildjar.sh
This may take awhile. You’ll only need to do it again if you ever change anything under ../infrastructure.
Run it
bin/run-local.sh
You should now be able to access your EtherPad locally through localhost:9000, but that won’t do anybody any good, especially since we’ve hardcoded your domain all through it.
Configure Apache to proxy
Setting up the proper DNS records and locking down your server is left as an exercise to the reader.
Create an apache virtual host file like /etc/apache/sites-enabled/001-etherpad
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<VirtualHost *:80>
ServerName etherpad.yourdomain.com
ServerSignature Off
ErrorLog /var/log/apache2/etherpad.yourdomain.com/error.log
LogLevel warn
CustomLog /var/log/apache2/etherpad.yourdomain.com/access.log combined
ProxyPass / http://localhost:9000/
ProxyPassReverse / http://localhost:9000/
ProxyPreserveHost on
<Proxy *>
Options FollowSymLinks MultiViews
AllowOverride None
Order deny,allow
Allow from etherpad.yourdomain.com
</Proxy>
</VirtualHost>
|
Create your logs directory:
sudo mkdir /var/log/apache2/etherpad.yourdomain.com
Make apache the owner:
sudo chown www-data:www-data /var/log/apache2/etherpad.yourdomain.com
Enable mod_proxy:
sudo a2enmod proxy
sudo a2enmod proxy_http
Restart Apache:
sudo /etc/init.d/apache restart
That’s it!
Collaboration is just a website away, all in the comfort of your own domain. Some features will be disabled, like email (unless you set up SMTP on your server) and a few of the export formats. Some have had success enabling more features. Try it out if you’re brave. It will be exciting to see how this develops as an open-source project.
EtherPad
Late in 2009 Google acquired AppJet, the company responsible for the popular collaberative Web-based text editing tool, Etherpad.
Presumably this is to beef up Wave’s feature set, which is way cool. Uncool though, was the announcement that free public “pads” could no longer be created, and the entire service would be discontinued on March 31, 2010. After much uproar, Google and EtherPad revised their stance, promising to leave the site up and free to the public until the entirety of the source code (and the AppJet platform running it) could be opened to the public, allowing anyone to host his or her own Etherpad instance.
Though the process is still in the early stages, many are already setting up their own EtherPad instances, getting ready for the day Google pulls the plug. I’m among these hordes, and I’ll probably toss up an Ubuntu server walk-through once I get mine fully working at etherpad.jordanhollinger.com.
Unobtrusive in-field text labels
Warning: This post concerns Web interface development and may be appear as arcane to many. It also contains HTML and JavaScript examples that may not render properly in feed readers or importers like Facebook. Follow the link back to the original post if you’re having trouble.
In HTML user-input forms, it is not uncommon to see text fields with “labels” as grayed-out text in the field itself which disappear when clicked.

My search for “best practices” on this technique left me…wanting. Firstly, I wasn’t sure what to call it. Secondly, many implementations were riddled with problems, paramount of which was abominable degradation. I am no accessibility expert, but these solutions didn’t even try. Here are two of the most common, and unfortunately, the worst.
Trickle-down
Initially this method looked promising. You explicitly set the value of the field to your label string which trickles down to the defaultValue attribute. You can then use defaultValue to determine when to clear or reset the label. Problems:
- Mean to screen-readers, non-JavaScript browsers, and those who use them
- Mean to your controller/form processing code (it somehow has to know to ignore your labels when they’re posted as values)
- Mean to forms that edit existing data (did defaultValue come from the label or from real data? If it’s real, there is no way to default back to the label if the user removes the value)
- Depending on the implementation it can be obtrusive
Some of these issues can be resolved through code, but it’s not going to be pretty.
Hidden fields
This, uh, solution, displays a “throw-away” text field as the label. On click, it’s replaced by the real field. To the user it appears that the text just disappeared. This has all the problems above plus some:
- Really really mean to screen readers and non-JavaScript browsers (there will be duplicate fields with text already in them for no apparent reason)
- Really mean to controller/form processing code (it has to arbitrarily ignore entire fields, not just values)
- It’s unbelievably intrusive
- It’s just a horrible idea on par with suggesting users place and remove labeled Post-it notes when moving from field to field
A better way
First, there may be an Even Better Way, but I believe this address all of the problems above, both practical and conceptual. In a nutshell, you set the field’s title, which you should be doing for accessibility reasons anyway. Optionally add a “real” label tag linked by id to it’s input field. JavaScript then looks for titled fields with blank values, hides their labels, sets the value to the title, and binds focus and blur events to the field. These respectively clear and reset the label (by comparing the value to the title) when users click on or off. On form submit, if any of these fields’ values match their titles, they are cleared by some JavaScript. Benefits:
- Unobtrusive
- Graceful degradation (screen-readers and non-JS browsers can fall back on the real label or title attribute)
- Uses all field attributes for their intended purposes
- Places all responsibility/logic in one location, removing the need for controllers to treat certain values or fields specially
- No Post-it notes
A possible draw-back is that users cannot submit a value that matches the field’s title. If that’s a real problem, you could probably add some more checks to hack around it. But how often is someone’s first name First?
My implementation uses >= jQuery 1.3. Since we’re on the Web anyway, I thought I’d throw in a working example below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
<form id="form_field_label_example" action="" method="post"> <p> <label for="first_name">First</label> <input id="first_name" name="first" size="15" title="First" type="text" /> <label for="middle_name">Middle</label> <input id="middle_name" name="middle" size="8" title="Middle" type="text" /> <label for="last">Last</label> <input id="last" name="last" size="25" title="Last" type="text" /> </p> </form> <script type="text/javascript"> $(document).ready(function() { auto_label("form#form_field_label_example input[title][type=text]") }) function auto_label(str) { $(str).each(function() { // Hide the field's real label $('label[for=' + this.id + ']').hide() // Set the label text and color for blank fields if ( !this.value || this.value == this.title ) { this.value = this.title $(this).css('color', '#999') } // On focus of blank fields, clear the label text and reset the color $(this).focus(function() { if ( this.value == this.title ) { this.value = '' $(this).css('color', 'inherit') } }) // On unfocus of blank fields, restore the label text and color $(this).blur(function() { if ( !this.value ) { this.value = this.title $(this).css('color', '#999') } }) }) // Clear label text when submitting form $(str).closest('form').submit(function() { $(str).each(function() { if ( this.title && this.value == this.title ) { this.value = '' } }) }) } </script> |
Neither a lender nor a borrower be
I don’t know with whom that adage originated, but it inappropriately presupposes two entities. What if your’re both a lender and a borrower…from yourself!
“What kind of double-entry chicanery are you pulling, Hollinger??” People who have heard me describe my budgeting system tend to think of it as…thorough. It consists of 23 line items, each of which I see as completely necessary. As a rule, every dollar in and out is recorded to or from one of these categories. (That’s tough enough as a single guy. Unless I marry an accountant, I may need to loosen up a bit someday.)
Anyway, this year I’ve been slowly saving up for a new car. Yes, there’s a temporary “New Car” line item. My current car is getting pretty long in the tooth, so either I keep saving and have Car Talk‘s number on hand, or I buy a clunker now from Uncle Bobs Big Bargain Blowout Bananza and still have Car Talk’s number on hand.
Because I want to have my car and eat it too, I’m choosing what’s behind Door Number 3: my “Don’t Use This Until the World Ends, And Even Then Only if the Dollar Remains Strong” line item. (It’s actually just called “Long-term Savings,” but that’s not as colorful.) I’m lending New Car a couple G’s from LTS, and will eventually pay myself back. I didn’t even require a credit check! I briefly considered slapping an interest rate on there, but decided that’s a little too nuts.
This may all become moot anyway, because Wilmore just legalized golf carts on the road. And that’s obviously a better choice.
I’m trading my current car for a TV, but that’s another story.
Breadcrumbs in Rails
I’ve recently been pondering the best practices of implementing breadcrumbs in a Rails app. Plugins and gems would be superfluous to such a trivial task, which leaves us to our own devices. I’ve read somewhere that specs can be helpful in designing a system, so I made some up:
- Each crumb should be a link, except for the last one, which denotes the current page
- It has to be flexible, allowing for many use cases
- The implementation should be simple and brief, but mostly simple
- The calls should be simple and brief, but mostly brief
- Brevity may decrease as the complexity of need increases, and as probability of need decreases
The following is how I mostly satisfied these. If you’re searching like I was, maybe you’ll find this helpful, or even perfect. Or maybe you’ll be horrified and post incendiary comments. “No, you cretin! Put the responsibility on the controllers, not the views!” or “Go back to PHP where you came from, like a dog returning to its vomit!” Ouch, guys. Be more constructive with your feedback.
Displaying breadcrumbs
Here’s part of my application.html.erb. (Tags have been removed for readability.) @breadcrumbs is just an array of arrays like ['History', '/about/history']. This differs little from other solutions, such as require ‘brain’ . We’ll get something like Home » About » History.
1 2 3 4 5 6 7 8 9 10 11 |
# If no breadcrumbs have been set, this calls drop_crumbs without # arguments, which causes it to parse request.path into @breadcrumbs drop_crumbs if @breadcrumbs.nil? # Print out the breadcrumbs as links, excepting the last one @breadcrumbs[0..-2].each do |text, path| link_to(h(text), path) + "»" end # Show the current page's crumb, but not as a link h(@breadcrumbs.last.first) # Force double arrows if we're on the root page "»" if @breadcrumbs.size == 1 |
Creating breadcrumbs
Believing that a system which automagically figures out all breadcrumbs all the time is impossible, and believing that a system which requires you to always specify them is stupid, I tried to write drop_crumbs with useful defaults and a mixture of implicit and explicit arguments.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
# Append one or more crumbs def drop_crumbs(*args) @breadcrumbs ||= [] args = [request.path] if args.empty? args.each do |crumb| crumbs = make_crumb(crumb) if crumbs[0].instance_of? Array @breadcrumbs = @breadcrumbs | crumbs else @breadcrumbs << crumbs end end end private # Returns a crumb array like ['Home', '/'] def make_crumb(crumb) return ['Home', '/'] if crumb.empty? if crumb.instance_of? String break_off_crumbs(crumb) elsif crumb.instance_of? Array return crumb if crumb.size > 1 return ['Home', '/'] if crumb[0].empty? title = crumb[0].split('/').last.split(/_|\s/).each { |word| word.capitalize! }.join(' ') unless crumb[0].nil? [title, crumb[0]] end end # Break a uri into an array of crumbs def break_off_crumbs(uri) crumbs = [] split_uri = uri.split('/') split_uri.each_with_index do |crumb, i| crumbs << make_crumb([split_uri[0..i].join('/')]) end crumbs end |
Useful defaults
drop_crumbs
If we’re at /contact/electronic/fax, this will automagically get us Home » Contact » Electronic » Fax. In some apps, that could cover most cases.
Simple implicit path arguments
drop_crumbs('/store/checkout')
This will give us Home » Store » Checkout. You could get the same result by passing ['Home', '/'], ['Store', '/store'], ['Checkout', '/store/checkout'], but that’s a waste unless you want to specify different names for the links.
Flexible mixed implicit and explicit arguments
drop_crumbs('/human_resources/employees', [@employee.first_name, employee_path(@employee)], ['Edit'])
Let’s say you’re at /human_resources/employees/24/edit. Maybe you want to give number 24 a raise. Maybe you want to fire him. Pick a scenario that makes you happy. This will give us Home » Human Resources » Employees » Alastair » Edit. If instead we had relied on the defaults, we would have seen 24 in place of Alastair, which is less meaningful.
Note that the final Edit crumb doesn’t have a path. Since it’s the last crumb and won’t be a link, we can safely ignore it.
I will admit this looks complicated for just replacing an id with a name. You may want to break it up into multiple drop_crumbs calls or write a drop_employee_crumbs wrapper that takes an employee for an argument. Or maybe there’s something much better I haven’t thought of yet.
This is my first shot ever at breadcrumbs, and I’m sure I’ll find improvements as I continue to explore the uses. Currently, my drop_crumbs and associated methods are helpers in ApplicationHelper. When I need to explicitly call drop_crumbs, I do so in my views. I can see an argument for moving those calls into controllers, but I’m still mulling that over.
In the meantime, If you’ve got a Breadcrumbs Blue Ribbon pinned to your chest, please share your secret.
Breakfast at Tudor's
I met a friend for breakfast this morning at Tudor’s Biscut World. If you’re not from West Virginia, you probably haven’t heard of Tudor’s. And you’re probably at less risk for heart disease.
To paint this picture, we’ll need plenty of bitter coffee, heavy accents, and cholesterol. It will be an oil-based painting. Lots of grease. This morning’s special was the “Big Tater,” an amalgam of fried potatoes, bacon, eggs, and sausage gravy topped with cheese. There are three of these greasy spoons in my hometown of 11,000. As their patrons steadily die off from massive coronaries, Tudor’s will eventually run themselves out of business.
I went with a simple sausage biscuit, but still left feeling as though I had put a brick in my stomach. Unless I don’t make it to tomorrow, Merry Christmas!