Digital Sharecropping

Before 1994, the Internet was basically unknown. It was just a tool for professors and researchers to connect with their peers. All websites had to be non-profit.

In 1994, the National Science Foundation took away these restrictions. Anyone could register a domain name and start a website, even to sell stuff. Pepsi.com was one of the first, but at the time it seemed a pointless gimmick.

Flash forward to 2008. In the past five years, power has become consolidated between a few major websites, despite the flat nature of the Internet. Google, Yahoo, Facebook, MySpace, and eBay are the major players. These corporations control billions of dollars in capital, yet with the exception of eBay, provide free services. How does this happen?

MySpace

The way it happens is through advertising. Much like how newspapers make money from the classifieds or how the local Pennysaver is completely free despite rising print costs, websites make money from selling ad-space. With technology like HTTP cookies and click-counting, advertisers can pay only when viewers click their ads, or even only when they make a sale. If you think no one buys anything online, take a look at this.

2007 Christmas online sales

That’s a graph of how much stuff people bought in the 2007 Christmas season. At the peak, for the week ending 2007-12-16, sales totaled nearly 5 billion dollars. Thanks to comscore.com for the stats.

As you can see, people have no aversion to buying things on the web. And unlike with newspapers, websites have far lower overhead. Each visitor costs less than a hundreth of a cent each, while advertisers may be willing to pay in dollars for clicks or sales.

The reason social networks have become so large and wealthy is because most people contribute to them for social benefits, while all the economic benefits go to the operators of the network. Many people may only generate a few dollars in revenue, but with millions of people it adds up. Also, people will join even a hard to use and poorly designed website if all their friends are on it, so the rich get richer.

MySpace has ads all over the place; their home page is one big ad as you can see, and when you log in it gets even worse. People use it anyway because so many people are already using it, not because of it has intrinsic value.

When you’re contributing to MySpace or Facebook or any other network you don’t control, you’re a sharecropper. But what is a sharecropper? This is a good definition.

“A farmer who works a farm owned by someone else. The owner provides the land, seed, and tools exchange for part of the crops and goods produced on the farm.”

Sharecropping on the Internet is even worse, because you don’t even get a portion of the fruits of your labor. You give up not only the means of production, but also all revenue earned and the information itself.

My Dad was banned from YouTube because he’d get into all sorts of political arguments with people there. Not only do they delete all your videos, but every comment you’ve ever made disappears from the site upon your removal. That’s what happens when you’re a sharecropper, and the owners are free to do that because it’s their website. If my Dad didn’t keep backups of everything he writes and posts, he would’ve lost it all.

We’re all sharecroppers for Google. Here’s just a few things they own:

Google's stuff

It’s hard to keep track of all these services, so they have this nice umbrella called the Google Account:

The Google account

Everything runs nicely for a while. You have all your maps, your credit card data (Google Checkout), your calendars, your emails, your search history, your contacts, your pictures, your blog posts, and more on Google’s servers. Then they decide they don’t like you anymore:

No more Google for you

Thanks for being a good sharecropper, we know longer need you. Good-bye. This is the message my Dad got when you tried to log into his YouTube account. Now, YouTube uses Google Accounts, so if he was banned now, his emails might vanish too.

Obviously, Google can’t go around banning all it’s members if they want success, but we’ve given them a lot of power. I don’t know about you, but I don’t like to give up my power, even in the name of convenience.

If you think it can’t happen, take a look at this: When Google Owns You. This guy was locked out of his email, documents, photos, and instant messaging, because Google shut down his entire account. He got it back eventually, but the real problem is that we’ve all given up our power.

Though our computers are more powerful than ever, we’ve become increasingly dependent on Other Peoples Computers. We let Google or Yahoo hold our email so we can get to it from anywhere. We put our pictures on Flickr or Snapfish or Picasa Web Albums so our family can see them from anywhere in the world. But they’re not on our computer, so Flickr or Snapfish or Google can take them down at any time.

Should the government force web corporations to share their profits or hand the means of production over to the people? I say no, because that is socialism and it would discourage new innovation. Like it or not, it’s hard to create infrastructures like Google or MySpace, which allow millions of people to share information for free.

you.com not myspace.com/you

The base-level infrastructure will always be the Internet and sites like you.com, not myspace.com/you. Don’t put much effort into your site on MySpace; start your own site.

Breaking the chains requires you to have a computer on all the time and a registered domain name. You also need software on the web server to manage your photos, text, video, or other content. These are good to start with:

Content management software

The best way to get a web server, when you’re starting out, is to rent one. You do this through what is called a web host, which costs about $10 a month. You also register your domain name through a registrar, just like MySpace and Facebook do. You have to pay $10 per year for that.

I use GoDaddy.com as my domain registrar and SYNhosting.com as my host. My whole blog and photo gallery is run by WordPress and other open-source modules, and it’s no more work than using MySpace, besides a large up-front investment of time and effort. I’m not sharecropping, because I can easily switch without losing my domain name if I get tired of either of these companies. If you’re a sharecropper and you switch landlords, forget about keeping the same URL.

Back up stuff

If you can’t do the above, there is an easy, immediate step you can safeguard yourself with. Back up your data. Whenever you write anything on a site you don’t own, copy it to a text or Microsoft Word file on your computer.

Thunderbird

If you use Gmail (owned by Google), use Mozilla Thunderbird to keep a duplicate copy of your email on your computer. Even if Google steals your emails, you’ll still have them on your machine. You can also use Microsoft Outlook Express with your Gmail account, and they even have tutorials on how to do it.

Flash drives

Instead of giving control of your documents over to Google, keep them on a flash drive. You can still get to them anywhere, because you can carry a flash drive with you all the time. Even better, you don’t need Internet access to get to your stuff. Your files are right here, not on some far-off server where they can be stolen or deleted on a whim. Make a backup copy on your computer at home whenever you change stuff, and you’ll be fine.

Moving away from your landlords is hard, but think of it this way: even if you get one-tenth the visitors to your new website and it looks like garbage, it’s still ten times better than continuing as a fruitless sharecropper. You can ever put ads on your site. I made $60 through Google’s AdSense program this month, and while you could say that I’m still sharecropping because I’m beholden to them, if they kick me out I can easily switch to Yahoo’s ad offering or I can sell ad space directly. If you’re on MySpace, you have no such options. There are plenty of ads, sure, but you get nothing for them, even if you become insanely famous.

You can’t be free as a sharecropper.

Your Blog is a Marching Wiki

When I think of a wiki, I think of a collection of articles that can be edited by anyone. But wikis have another core trait. If you’ve ever looked up an article on Wikipedia, you’ve noticed that practically every other word is a link to related articles in the wiki.

There are no direct links to external sites. All those are footnotes or references, appearing at the bottom of the page. But within the text, there are internal links all over the place. It’s a self-contained Internet.

I think your blog should be the same way. This isn’t reasonable until you’ve built up a good collection of content—perhaps thirty articles at least. But once you’ve done that, you should start linking to them whenever relevant. When I talk about artistic photography, I’ll link to my gallery, and when I talk about happiness, I’ll probably link to How to Be Happy. And when I talk about linking, darn it, I’ll link to The Perils of Redundant Linking. These links are redundant to people who read my whole blog from start to finish, but those people can just ignore the links. The larger majority skims two or three of my articles to take in the essential points, and for them, the links are invaluable, because they connect them with other subjects of interest. Because the links are contextual and manually added by me and me alone, they’re better and more relevant than what any search engine or group of people can offer.

I believe in subjective reality / multiple truths. Wikis are disconcerting because they try in vain to represent an objective reality by synthesizing and representing the beliefs of hundreds of people. Sometimes, it works, but within the whole wiki you always see incongruity. Certain articles read like advertisements, others are comical, others are dead serious. Some use weak language and weasel words like “may have,” “possibly,” and “back in the day,” while others try to be overly-precise, to the point of being inaccurate. I could say John Lennon was killed at 1980-12-08T22:52:52-05, and it would be very precise, but it wouldn’t be accurate. Even if I am accurate, my accuracy is unprovable. The point is, no two people have the same perspective on wording or accuracy. When you merge too many perspectives, you end up with a muddled mess. Sure, like Wikipedia, you can still be informative, but it’s nonetheless a mess. There’s room for someone else to come along with a clear vision and really share expertise with others. Committees don’t do this. One person alone, having synthesized the perspectives of the world in a way more congruent than any collection of people, shares knowledge more compelling and evolved than all else.

Your blog should be a marching wiki, meaning it marches forward without looking back. Ordinary wikis do not march. Old articles are constantly being revised, updated, and perfected. Many bloggers and photographers refuse to let go. They spend so much time revamping old stuff, they never create anything new. If you’re drafting a book, this is fine. But like publishing a book, I consider posting an article to my blog a singular act representing your beliefs and knowledge at a fixed point in time. Unless I find a typo, or a broken link, or I write something new that expounds heavily upon the topic, I don’t update the old post. When I update the old post, it’s just to correct those errors or add a link to the new post.

Substantially changing the content of old articles can be a good thing for your readers, but that doesn’t make it worthwhile. Your time is much better spent putting what you’ve learned into a congruent, fine-tuned, new work of art, rather than adding bells to an old piece. Rewriting your archives can even be a disservice to your readers, because those articles show your history and your beliefs at a previous point in time. Do you dare erase your past? Would you rewrite and dress up an essay from middle school for a college assignment? No—you’d write something new entirely, and it would be much more evolved than your old work.

If you strive for a faster pace of evolution in your persona and your writing, tending to your old work will seem as unusual as tracing your drawings from kindergarten. Sure, tracing your childhood sketches would garner you experience, but the experience of creating anew is far greater than dwelling in the past. Our time is limited in this life, so it is important to optimize our learning processes as far as possible.

Similarly, don’t go back to old articles to add links to new articles, unless it’s something really important. I’ve done it about ten times, and considering I’ve been blogging for eight months and have written hundreds of posts, that isn’t a lot. While I could go back to Investment and Efficiency and add a whole bunch of (relevant) links to newer work, including this article, it would actually distract my readers. Simply put, if I wanted to link to newer stuff in the older article, I would’ve written the newer stuff first. I didn’t, so the older stuff doesn’t need to reference the future.

When you establish yourself as a soldier on the march, you lift a great weight off your shoulders. No longer must you worry about maintaining continuity with the past. In fact, I encourage you to openly contradict your past—should it represent the evolution of your opinions, or a different perspective that is valuable to your readers. Don’t feel you have to explain yourself. Don’t write for the critics. Most people aren’t trying to shoot holes through your work; they want to share in the wealth of your knowledge. They’re just like you. It’s far more important to cater to the important people, rather than a vocal, critical minority.

Abandoning continuity is most important for beliefs, but also includes presentation. In some articles, I highlight key points in italics; in others I use bold. In my school essays, I underline. Sometimes I’ll use inline bold headings like this, while other times I’ll use large headings bounded by line breaks, which really draw the eye. In some articles, I highlight nothing, because everything is important. I don’t have to make a list of rules for myself to govern these processes. I do whatever feels right in the present context. It doesn’t matter if it doesn’t match up exactly with my history.

Let your beliefs and standards be fluid like water, changing to meet the demands of different terrain, rather than rigid and inflexible like ice. Your important beliefs may turn to ice, but even ice can melt or break. If this happens, don’t resist it—recognize it as inevitable change: the only way to transcend your current level.

Finally, don’t create lists of rules for yourself. There are plenty of other people who are happy to do this for you; you don’t need even more rules. You are your own boss, and you control your own destiny.

Money and Love

What I made online, 2008-08

Just wanted to give you a little hint for how my websites did last month. My goal is $1 per day, and while I didn’t hit that every day last month, the overall total was $56.41, or $1.80 per day.

I can see I’m making a bigger impact on the world. In July, I made $20, so my income basically tripled last month. You can’t get that kind of raise with a regular job.

$53.73 was from Google AdSense; $2.68 was from this blog’s Amazon Associates commissions.

Of the $53.73, $1.54 came from Brilliant Photography and Personal Development by Richard X. Thripp. Th8.us made $2.30, DaytonaState.org made $42.63, and the Thripp.com users made me $7.26. Our hosting / domains bill is $15 per month, so I’m more than covered.

DaytonaState.org is targeted. A lot of people come from Google looking for information on enrolling in colleges, so the information appeals to them. On the other hand, Thripp.com is black-listed by Google, so this blog and others appear low in the search results. I haven’t done much marketing either, instead focusing on writing and producing new works of art, so that explains the low turnout. But in the long run, the richardxthripp.thripp.com is where the action is going to be.

If I was still working at the library, I would have made ten times more. But last month, I did no work. Even though I exhausted far more time and energy here, the energy comes back twofold. This simply isn’t a job at all to me.

When you’re doing something you love, it doesn’t matter how much time you “spend” or how much money you make. (You’re not really spending time; you’re saving it.) You’ll do it anyway, because it’s what you do. If you’re looking for something to love, photography is a good place to start, even as a freelancer. I wouldn’t do it myself; I’d prefer writing articles for free and making money indirectly. But every person enjoys different methods.

Also: when you love playing with LEGOs or battling in World of Warcraft, it still doesn’t matter, but the problem is you’re not helping anyone else. You think you’re doing what you love, but you just haven’t found what you truly love. When you combine ambition with a purpose that really makes an impact on people around you, you’ll find something you love far more than child’s play. It’s still play, but it’s play taken to the next level. You character has just reached level 100. He’s replaced hit points with love points.

How to Create a Public Library

I’ve disappeared for the last few days because I’ve been working on The Thripp Public Library. While I can’t open it to the public yet (Dad won’t open our house to the world), I’m working on it now because I have time off and Dad’s generously donated lots of his books. Though I wanted to use Evergreen or Koha, I picked the simple and obscure OpenBiblio as my library system, because it’s the only thing I could find that would run on shared hosting. I was disappointed by the lack of features to start, but I’m starting to like the power and control with it, especially since the database makes sense, so adding new features is easy.

Before I even got started, the first step was to choose a barcoding format, classification system, and spine labeling format.

I don’t like Library of Congress (LC) classification because it’s arcane and confusing, so the Dewical Decimal system (DDC) was the default choice. But what to use it for? You can use it for everything, but I decided right away not to use it on fiction items, instead opting for “FICTION / *last name*” as the spine label and call number, which is the same as the Volusia County library system. It may not be ideal, but it’s much easier to use. Biographies are tougher. I chose DDC, but I put two lines above that say “BIO / *subject last name”. The DDC part is always “*numbers* *first three letters of the author’s last name*”. Large type items get “LT” at the top of the spine label. Spine labels == call numbers, always. I created a template file for spine labels in OpenOffice.org Writer, which I add to as I catalog books. Then when I get to twenty or thirty, I print out the whole sheet and start cutting them out with scissors and taping them to the books. Here’s part of a recent sheet:

Lots of spine labels

The numbers after the dot are sometimes six or more in Dewey Decimal classification. You can truncate (cut off) them, but I let them stay and just wrap the label around the book.

The next big step is barcoding. I don’t have a reader yet (too expensive), but it’s good to be ready ahead of time, and it gives a unique identifier for each item. Many libraries, including Volusia County, follow the format F LLLL XXXX XXXXC, where F is the flag, 2 for patrons or 3 for items, LLLL is the system identifier (2417 for Volusia), XXXX XXXX is an incremented number, and C is the check digit. The symbology is Codabar. This is nice, but the institutional identifiers are only useful if your part of a larger organization. I can’t find who oversees them, nor a list of each county / system and it’s code. If I picked one for my library, no one would respect it, plus it makes the barcodes unnecessarily long. So “normal” barcodes are out.

Codabar is old, and there’s no reason to use it if you aren’t follow the 14-digit format. So I picked the more robust Code 39. I looked in vain for open-source or free software that makes it easy to print up sheets of barcodes while generating the check digits, but gave up in disgust. I’m just doing it in OpenOffice.org Writer with this free Code 39 font. No check digits. My barcodes are only eight digits, and check digits aren’t needed anyway because the symbology is self-checking (so I’m told). Plus, forgoing check digits makes things much easier. I have a template of 5 pages with 20 barcodes each, numbered 1-100. When I want to print new ones, I can just find and replace every instance of “300300” with “300301” and it’s all done instantly. Here’s what that template looks like:

Lots of barcodes

This is awesomely cool, and it is a robust solution, even if it seems too simple. I invite you to use this template to print similar barcodes. Make sure you have the font installed first, as this .odt document expects it. I just print these on card stock, cut them out with scissors, and affix them to the books with clear packing tape. I thought I had to decide to put the barcodes on the outside of the books or the inside… but I have the best of both worlds! I printed two copies of the template and put a barcode in both places. This way, I have the convenience of outside barcodes, but my items can still be identified if the barcodes peel off or the covers are destroyed. Clear tape over the barcode won’t matter for any scanner worth its salt. These barcodes are big and easy-to-scan too. I don’t know why most libraries use ones so small.

My numbering format is eight digits; two groups of four. The font doesn’t allow a space between them, but they are visually separated by the zeroes. I start patrons with 2 and items with 3 like traditional library barcodes. The format is 2001XXXX for patrons and 3003XXXX for items. So far I have 5 patrons and 81 items, so the highest patron barcode is 20010005, and for items, 30030081. When I get to 9999, I’ll go up to 2005 and 3007. There’s no reason the last 7 digits of patrons vs. items need to ever clash, and no overlap makes it easy to use abbreviated barcodes for internal memos.

A record number is created by OpenBiblio when you add an item. It’s just a numeric counter starting at one. The record number is used in OpenBiblio’s URLs, and is an easy identifier for my patrons to communicate to me. Barcodes are good too, but the problem is that they are transient, while record numbers are static (as long as I don’t delete the record). Also, the record number stands for the whole record, while a barcode is just for one item. There could be 10 copies of one item, but there still is one record number. So it makes sense to divorce barcodes from record numbers.

Of course, since I’m printing barcodes myself on a home laser printer, there’s no reason for barcodes to be transient. If a patron loses his library card, I just print up a new one on the spot with the same barcode. Same for damaged barcodes on books. But I can also replace the code with a new one if that’s quicker or easier for me, if I use record numbers as the unique identifier for the record (instead of the first barcode or nothing). While OpenBiblio makes similar numbers for patrons, mainly to distinguish them in the MySQL tables, I see no reason to use them. Each patron will only ever have one barcode. Even if I offer keychain library cards, they’ll have the same numbers as the big versions.

Before we go on to cataloging, let’s take a look at library cards. I took what I learned from item barcodes and applied it here. I made a cut-out template in my graphics software, which I’m using to overly text onto in OpenOffice.org. A sheet of library cards looks like this:

Lots of library cards

Here’s the library card template (font required). To change things, I right-click the background image, click Wrap > No Wrap, go to page 2 and change the numbers or other text, then go back to page one, right-click the image again, choose Wrap > In Background, then print. This is in OpenOffice.org 2.2.0. I haven’t upgraded to the new version, but it should be the same. To use them, I print them on card stock (a.k.a. matte photo paper), cut them out with scissors, then laminate them with packing tape (carefully). Of course I enter them into OpenBiblio too. I created custom fields under Admin > Member Fields, for alternate library cards, driver’s licenses, notes, and dates of birth. Those look like this:

OpenBiblio custom fields

The logic with the alternate cards is this: a patron can get a card and add his Volusia and/or Flagler cards, so he can use those instead if he forgets his Thripp card. If he does that, I look him up by name because OpenBiblio won’t allow searching by custom fields (I may fix this later), and then approve the alternate card if the numbers match. Primary Card Origin is different; a patron can choose to use only his Volusia, Flagler, or other library card to identify himself in the Thripp system, in which case I can use the built-in barcode search on the third-party card, and the patron doesn’t even need a Thripp card. In that case, I type in the origin of the primary card in that special field (i.e. “Volusia”, “Flagler”, etc.).

Now that we have labels, barcodes, and cards out of the way, the next step is cataloging. I didn’t want to do all the work myself; instead I get some of the cataloging data from the Library of Congress. The problem with this is the LoC Lookup Patch won’t work with SYN Hosting because they won’t enable the PHP YAZ module because of security concerns. I tried the alternate LoC SRU, but I get this error:

Warning: fsockopen() [function.fsockopen]: unable to connect to z3950.loc.gov:7090 (Permission denied) in /home/richardx/public_html/lib/catalog/locsru_search.php on line 98

Notice: Socket error Permission denied (13) in /home/richardx/public_html/lib/catalog/locsru_search.php on line 125

I gave up on direct import and went to USMARC files. This was hard to figure out. The way to do it is to get MarcEdit 5.1. In the software, go to Add-ins > MarcEdit Z39.50/SRU Client, go to Modify Databases, click Add Database > Import from Master List, click the second in the list (Library of Congress), click Select Resource, go back to Search Mode, click Select Database, double-click Library of Congress, search for something, and double-click the item you want to import from the results. Then, click Download Record. Rinse and repeat, using the “Append” option. After you have, say, 30 records, go to Cataloging > Upload Marc Data back in OpenBiblio, and upload the files. It should say that 30 records are added, and then you can polish the data by searching and editing under Cataloging > Bibliography Search, book by book.

I edit the data to format the title my way, clear junk from the ISBN field, make the extent field a page counter, add the cover price as cost, and create the call number based on the Dewey Decimal code. Unfortunately there’s a lot of junk like “BOOKS” and “Copy 1” in the LoC Marc records, and I haven’t found a way to filter them out. I went through the MySQL database and cleared a lot of them recently.

This still saves me a lot of time, because coming up with the information myself would be too much work. The search doesn’t work well. If ISBN fails, I try title or author, or I go to the online search and get the control number to search by as “Record Number” in MarcEdit.

Sometimes there is no Dewey Decimal classification; just Library of Congress. I hunt down the item at another library in WorldCat to see what dot code they used. It’s easier than coming up with it on my own, and I wouldn’t get it right anyway.

As an alternate for when the Library of Congress has nothing, I installed the Amazon Lookup Module, which actually works. It just gets a few things like title, page count, publication date, author, and sometimes DDC code, but it helps.

Note that when I say “install,” this isn’t your typical WordPress plugin installation. This is getting down and dirty adding and editing pieces of code. Some modules are even distributed as hard-to-use-on-Windows .patch files, which scamper about editing two-dozen files in the OpenBiblio core. I’ve changed so much stuff, that when I upgrade to the next version, I’ll be merging the author’s changes with mine rather than mine with the author’s. It’s a completely different mindset.

Cataloging constructs are yours to set. Unfortunately, it’s rather inflexible because you have to work on a per-record basis, but this is expected with ILS’s. The standards are lower than with photo-cataloging software, because librarianship is considered a full-time job (time is cheap) and most libraries have fewer books than I have photos. I try to get things right the first time, meaning I use consistent formatting like putting a period at the end of the extended title and after the author’s name, keeping the ISBN field clean, using consistent capitalization, etc. This is a typical record. I let a lot of the Library of Congress’ stuff stay the same to save time, but the fields that I’m picky about are the ones that are shown in search results. Speaking of which…

This is the default search system:

OpenBiblio old search

And this is mine:

OpenBiblio new search

And mine has this, too:

OpenBiblio scoping

I realize no one knows what “federated” and “scoping” mean, but they’re such cool words I don’t care. You’ll learn it if you use my OPAC. OPAC stands for online public access catalog, for those of you not familiar with LIS jargon. LIS is library information science, and the OPAC is part of the ILS, or integrated library system. For scoping and federated search, I applied the Advanced Search by Title, Collection, Material Type .patch file using TortiseSVN, then modified it to fit my needs by removing the material type field (because it’s the same as collections in my library), changing “Search All” to “Federated,” and adding barcode and call-number search to it. This is an easy MySQL query addition, because the barcodes and call numbers are stored right in the biblio table in the database.

I like how the search works, because it matches partial words. I can type in “mel” as an author search, and I’ll get Typee by Herman Melville, which is the only book by him in my catalog right now. The Call Number search is good because I put useful data in the call number: “LT” for large type, “BIO” for biographies, “J DVD” for kids’ movies, “FICTION” for fiction, etc. So you can search by it. I haven’t figured out how to implement boolean queries yet, but what I have is pretty good.

This is the code behind the search. It’s in opac/index.php and shared/biblio_search.php, to be displayed below the search results so you can search again right from there. If you use it, do it after applying the patch I mentioned. This ASSUMES that you’re using mod_rewrite to change shared/biblio_search.php to search-results. Change action=”../search-results” to action=”../shared/biblio_search.php” on line 1 if the assumption is false.

<form name=”phrasesearch” method=”POST” action=”../search-results”>
<table class=”primary”><tr><th valign=”top” nowrap=”yes” align=”left”>Search the Catalog</td></tr><tr><td nowrap=”true” class=”primary”><select name=”searchType”>
<option value=”all” selected>Federated
<option value=”title”>Title
<option value=”author”>Author
<option value=”subject”>Subjects
<option value=”barcodeNmbr”>Barcode
<option value=”callnmbr”>Call Number
</select>
<input type=”text” name=”searchText” size=”55″ maxlength=”256″>
<input type=”hidden” name=”sortBy” value=”default”>
<input type=”hidden” name=”tab” value=”<?php echo H($tab); ?>”>
<input type=”hidden” name=”lookup” value=”<?php echo H($lookup); ?>”>
<input type=”submit” value=”Search!” class=”button”>
</td></tr></table><br /><table class=”primary”><tr><th valign=”top” nowrap=”yes” align=”left”>Advanced: Scoping (Optional)</td></tr><tr><font class=”small”><td nowrap=”true” class=”primary”><script type=”text/javascript” language=”JavaScript”>
function selectAll(ident) { var checkBoxes = document.getElementsByName(ident); for (i = 0; i < checkBoxes.length; i++) { if (checkBoxes[i].checked == true) { checkBoxes[i].checked = false; } else { checkBoxes[i].checked = true; } } }</script>
<input type=”checkbox” name=”selectall” value=”select_all” onclick=”selectAll(‘collec[]’);”>Flip<br /><?php $dmQ = new DmQuery(); $dmQ->connect(); $dms = $dmQ->get(“collection_dm”); $dmQ->close(); foreach ($dms as $dm) { echo ‘<input type=”checkbox” value=”‘.$dm->getCode().'” name=”collec[]”> ‘.H($dm->getDescription()).”<br />n”; } ?></td></tr></font></table></form>

I compressed some of it to one line, to make it really hard to read, because I like making things harder than necessary. :cool: It makes it easy to scroll through in the file, and I shouldn’t need to change that part. If I do, I’ll just look through it carefully. It seems to make more sense to my brain than regular, fluffy code.

In shared/global_constants.php, I have this:

define(“OBIB_SEARCH_BARCODE”,”1″);
define(“OBIB_SEARCH_TITLE”,”2″);
define(“OBIB_SEARCH_AUTHOR”,”3″);
define(“OBIB_SEARCH_SUBJECT”,”4″);
define(“OBIB_SEARCH_NAME”,”5″);
define(“OBIB_SEARCH_ALL”,”6″);
define(“OBIB_SEARCH_CALLNMBR”,”7″);

The beginning of the search function in my classes/BiblioSearchQuery.php file looks like this:

function search($type, &$words, $page, $sortBy,
$collecs=array(), $materials=array(), $opacFlg=true) {
# reset stats
$this->_rowNmbr = 0;
$this->_currentRowNmbr = 0;
$this->_currentPageNmbr = $page;
$this->_rowCount = 0;
$this->_pageCount = 0;

# setting sql join clause
$join = “from biblio left join biblio_copy on biblio.bibid=biblio_copy.bibid “;

# setting sql where clause
$criteria = “”;
if ((sizeof($words) == 0) || ($words[0] == “”)) {
if ($opacFlg) $criteria = “where opac_flg = ‘Y’ “;
} else {
if ($type == OBIB_SEARCH_BARCODE) {
$criteria = $this->_getCriteria(array(“biblio_copy.barcode_nmbr”),$words);
} elseif ($type == OBIB_SEARCH_AUTHOR) {
$join .= “left join biblio_field on biblio_field.bibid=biblio.bibid ”
. “and biblio_field.tag=’700′ ”
. “and (biblio_field.subfield_cd=’a’ or biblio_field.subfield_cd=’b’) “;
$criteria = $this->_getCriteria(array(“biblio.author”,”biblio.responsibility_stmt”,”biblio_field.field_data”),$words);
} elseif ($type == OBIB_SEARCH_SUBJECT) {
$criteria = $this->_getCriteria(array(“biblio.topic1″,”biblio.topic2″,”biblio.topic3″,”biblio.topic4″,”biblio.topic5”),$words);
} elseif ($type == OBIB_SEARCH_ALL) {
$criteria =
$this->_getCriteria(array(“biblio.topic1″,”biblio.topic2″,”biblio.topic3”,
“biblio.topic4″,”biblio.topic5”,
“biblio.title”,”biblio.title_remainder”,
“biblio.author”,”biblio.responsibility_stmt”,
“biblio.call_nmbr1″,”biblio.call_nmbr2″,”biblio.call_nmbr3″,”biblio_copy.barcode_nmbr”),$words);
} elseif ($type == OBIB_SEARCH_CALLNMBR) {
$criteria = $this->_getCriteria(array(“biblio.call_nmbr1″,”biblio.call_nmbr2″,”biblio.call_nmbr3”),$words);
} else {
$criteria =
$this->_getCriteria(array(“biblio.title”,”biblio.title_remainder”),$words);
}

And finally, this is the code that interprets the posted data, in shared/biblio_search.php:

#****************************************************************************
#* Retrieving post vars and scrubbing the data
#****************************************************************************
if (isset($_POST[“page”])) {
$currentPageNmbr = $_POST[“page”];
} else {
$currentPageNmbr = 1;
}
$searchType = $_POST[“searchType”];
$sortBy = $_POST[“sortBy”];
if ($sortBy == “default”) {
if ($searchType == “author”) {
$sortBy = “author”;
} else {
$sortBy = “title”;
}
}
$searchText = trim($_POST[“searchText”]);
# remove redundant whitespace
$searchText = eregi_replace(“[[:space:]]+”, ” “, $searchText);
if ($searchType == “barcodeNmbr”) {
$sType = OBIB_SEARCH_BARCODE;
$words[] = $searchText;
} else {
$words = explodeQuoted($searchText);
if ($searchType == “author”) {
$sType = OBIB_SEARCH_AUTHOR;
} elseif ($searchType == “subject”) {
$sType = OBIB_SEARCH_SUBJECT;
} elseif ($searchType == “all”) {
$sType = OBIB_SEARCH_ALL;
} elseif ($searchType == “callnmbr”) {
$sType = OBIB_SEARCH_CALLNMBR;
} else {
$sType = OBIB_SEARCH_TITLE;
}
}

// limit search results to collections and materials
$collecs = array();
if (is_array($_POST[‘collec’])) {
foreach ($_POST[‘collec’] as $value) {
array_push($collecs, $value);
}
}
$materials = array();
if (is_array($_POST[‘material’])) {
foreach ($_POST[‘material’] as $value) {
array_push($materials, $value);
}
}

Notice that I added CALLNMBR and BARCODE search, the logic for which was enumerated in classes/BiblioSearchQuery.php. It’s a good feature for my patrons to find an on-hand item in the OPAC, and for me it’s especially helpful.

I revamped OpenBiblio’s search results. A typical result looks like this:

OpenBiblio new search result

Compare to the old style:

OpenBiblio old search result

Notice all the new stuff in the top image? The link is bold. The extended title is below. No more small text. Material is gone (my collections’ titles make it self-evident). The record number is shown. The call number is important, so it’s bolded purple, and on the same line as the collection to save space. On Shelf status is bold and green; a great visual cue. It’s not “checked in” anymore, it’s the more sensible “On Shelf”. I changed that right in the database, in the biblio_status_dm table. Most importantly, do you notice the wonderful info line? Everything you could ever want to know is right there. It’s year / ISBN / pages or minutes / cost / Amazon.com link / # of circulations. That really puts a lot of power into the hands of your patrons.

I wrote/modified the code for my set-up, so you’ll have to change some things if you want to use it. This goes in shared/biblo_search.php, above the footer. Here it is:

<tr>
<td nowrap=”true” class=”primary” valign=”top” align=”center” rowspan=”2″>
<?php echo H($biblioQ->getCurrentRowNmbr());?>.<br />
<a target=”_blank” href=”http://lib.thripp.com/<?php if ($tab == “cataloging”) echo “e/”.HURL($biblio->getBibid()); else echo HURL($biblio->getBibid());?>”>
<img src=”../images/<?php echo HURL($materialImageFiles[$biblio->getMaterialCd()]);?>” width=”20″ height=”20″ border=”0″ align=”bottom” alt=”<?php echo H($materialTypeDm[$biblio->getMaterialCd()]);?>”></a>
</td>
<td class=”primary” valign=”top” colspan=”2″>
<table class=”primary” width=”100%”>
<tr>
<td class=”noborder” width=”1%” valign=”top”><strong><?php echo $loc->getText(“biblioSearchTitle”); ?>:</strong></td>
<td class=”noborder” colspan=”3″><strong><a target=”_blank” href=”http://lib.thripp.com/<?php if ($tab == “cataloging”) echo “e/”.HURL($biblio->getBibid()); else echo HURL($biblio->getBibid());?>”><?php echo H($biblio->getTitle());?></a></strong>
<?php $bid = HURL($biblio->getBibid());
$getxtitle = mysql_query(“SELECT title_remainder FROM biblio WHERE bibid = ‘$bid'”) or die(mysql_error());
$printxtitle = mysql_fetch_row($getxtitle);
if ($printxtitle[0] == “”) echo “”; else echo “<br />$printxtitle[0]”; ?></td>
</tr>
<tr>
<td class=”noborder” valign=”top”><strong><?php echo $loc->getText(“biblioSearchAuthor”); ?>:</strong></td>
<td class=”noborder” colspan=”3″><?php if ($biblio->getAuthor() != “”) echo H($biblio->getAuthor());?></td>
</tr>
<tr>
<td class=”noborder” valign=”top” nowrap=”yes”><strong>Ref. #<?php echo HURL($biblio->getBibid()); ?>:</strong></td>
<td class=”noborder” colspan=”3″><?php echo H($collectionDm[$biblio->getCollectionCd()]);?> / <strong><font color=”#640064″><?php echo H($biblio->getCallNmbr1().” “.$biblio->getCallNmbr2().” “.$biblio->getCallNmbr3()); ?></font></strong></td></tr><tr><td class=”noborder” valign=”top”><strong>Info:</strong></td><td class=”noborder” colspan=”3″>

<?php // RXT 20080723 code:
$bid = HURL($biblio->getBibid());
$getyear = mysql_query(“SELECT field_data FROM biblio_field WHERE tag = ‘260’ AND subfield_cd = ‘c’ AND bibid = ‘$bid'”)
or die(mysql_error());
$getisbn = mysql_query(“SELECT field_data FROM biblio_field WHERE tag = ’20’ AND subfield_cd = ‘a’ AND bibid = ‘$bid'”)
or die(mysql_error());
$getpages = mysql_query(“SELECT field_data FROM biblio_field WHERE tag = ‘300’ AND subfield_cd = ‘a’ AND bibid = ‘$bid'”)
or die(mysql_error());
$getminutes = mysql_query(“SELECT field_data FROM biblio_field WHERE tag = ’20’ AND subfield_cd = ‘c’ AND bibid = ‘$bid'”)
or die(mysql_error());
$getcost = mysql_query(“SELECT field_data FROM biblio_field WHERE tag = ‘541’ AND subfield_cd = ‘h’ AND bibid = ‘$bid'”)
or die(mysql_error());
$getamz = mysql_query(“SELECT field_data FROM biblio_field WHERE tag = ‘970’ AND subfield_cd = ‘a’ AND bibid = ‘$bid'”)
or die(mysql_error());
$printyear = mysql_fetch_row($getyear);
$printisbn = mysql_fetch_row($getisbn);
$printpages = mysql_fetch_row($getpages);
$printminutes = mysql_fetch_row($getminutes);
$printcost = mysql_fetch_row($getcost);
$printamz = mysql_fetch_row($getamz);
if ($printyear == “”) echo “Year Unknown”;
else echo $printyear[0];
echo ” / “;
if ($printisbn == “”) echo “ISBN Unavailable”;
else echo “ISBN: “.$printisbn[0];
if ($printpages == “”) echo “”;
else echo ” / “.$printpages[0];
if ($printminutes == “”) echo “”;
else echo ” / “.$printminutes[0];
echo ” / $”.$printcost[0];
if ($printamz == “Not on Amazon.com”) echo “”;
elseif ($printamz != “”) echo ” / <a target=”_blank” href=”http://www.amazon.com/exec/obidos/ASIN/”.$printamz[0].”/brilliaphotog-20″ title=”See this item on Amazon.com”>Amazon.com</a> / “;
elseif ($printisbn != “”) echo ” / <a target=”_blank” href=”http://www.amazon.com/exec/obidos/ASIN/”.$printisbn[0].”/brilliaphotog-20″ title=”See this item on Amazon.com”>Amazon.com</a> / “;
else echo ” / “;
$getCircs = mysql_query(“SELECT COUNT(bibid) FROM biblio_status_hist WHERE bibid = ‘$bid'”) or die(mysql_error());
$getCircsRes = mysql_fetch_row($getCircs); if ($getCircsRes[0] == ‘1’) echo ” (1 circ)”; else echo ” ($getCircsRes[0] circs)”; ?>

</td></tr></table></td></tr>
<?php
if ($biblio->getBarcodeNmbr() != “”) {
?>
<tr>
<td class=”primary” ><strong><?php echo $loc->getText(“biblioSearchCopyBCode”); ?></strong>: <?php echo H($biblio->getBarcodeNmbr());?>
<?php if ($lookup == ‘Y’) { ?>
<a href=”javascript:returnLookup(‘barcodesearch’,’barcodeNmbr’,'<?php echo H(addslashes($biblio->getBarcodeNmbr()));?>’)”><?php echo $loc->getText(“biblioSearchOutIn”); ?></a> | <a href=”javascript:returnLookup(‘holdForm’,’holdBarcodeNmbr’,'<?php echo H(addslashes($biblio->getBarcodeNmbr()));?>’)”><?php echo $loc->getText(“biblioSearchHold”); ?></a>
<?php } ?>
</td>
<td class=”primary” ><strong><?php echo $loc->getText(“biblioSearchCopyStatus”); ?></strong>: <?php $status = H($biblioStatusDm[$biblio->getStatusCd()]); if ($status == ‘On Shelf’) echo “<strong><font color=”#009900″>On Shelf</font></strong>”; elseif ($status == ‘On the Shelving Cart’) echo “<strong><font color=”#FF8000″>On the Shelving Cart</font></strong>”; else echo “<strong><font color=”#FF0000″>$status</font></strong>”; ?></td>
</tr>
<?php } else { ?>
<tr>
<td class=”primary” colspan=”2″ ><?php echo $loc->getText(“biblioSearchNoCopies”); ?></td>
</tr>
<?php
}
}
}
$biblioQ->close();
?>
</table><br />
<?php printResultPages($loc, $currentPageNmbr, $biblioQ->getPageCount(), $sortBy); ?>

This code assumes you’re using mod_rewrite to create friendly permalinks, in the format of YOURSITE/BIBID and YOURSITE/e/BIBID for the cataloging section. I’ll tell you how later in the article. It also assumes your site is http://lib.thripp.com and your Amazon.com affiliate code is brilliaphotog-20. Change the first one definitely. Leave the second alone if you want to donate to me. :cool:

This code also makes the assumption that your using my cataloging methods I defined earlier (year field, ISBN, pages are clean, etc.). For DVDs to show the minute count instead of pages, the number MUST be in “Terms of availability:”, which is the field I chose. You can change it easily if you examine the database structure and modify the code to fit your methods. The code defines the Amazon.com ASIN as the text in the ISBN field of the record, so your ISBN fields MUST be ISBN-10 and MUST be clean (no “(pbk.)” after the numbers). I’ve defined a contingency method: create a Marc field with tag 970, subfield a, with the ASIN if it differs from the ISBN or there is no ISBN. That will be used instead. If you enter “Not on Amazon.com” as the Marc field, no Amazon.com link will show even if there is an ISBN.

“On Shelf” statuses are shown in bold green, “On the Shelving Cart” is bold orange, and everything else is bold red. Make sure to change the statuses to those in the biblio_status_dm table, or do the opposite in the code. The number of circs includes checkouts and renewals for all copies attached to the record, past and present. Links open in new windows (I added target=”blank”). This is because the search results page uses POST data instead of URL parameters, so opening in the same window and clicking back prompts a warning. I wish it used URL parameters instead.

I like my search results format. It’s a lot more useful than what I see at most libraries.

I also upgraded shared/biblio_view.php to this:

OpenBiblio new item view

A typical record before would be this:

OpenBiblio old item view

Examples for the old style are from the Frances D. Still Learning Center OPAC. They use a stock OpenBiblio install. Nearing 3000 items. OpenBiblio scales failry well.

The code for the record view page is mostly copied from the search results page, so I won’t copy it for brevity.

I created a robust statistics system on the home page, which goes around aggregating numbers in the database so that it’s always up-to-date. It has one huge flaw: it assumes each record has one and only one item. Mine do, so it isn’t a problem, but I’ll have to re-work it when that changes.

The code requires you to create config.php and global.php in the OpenBiblio root. config.php should look like this:

<?php unset($config);
$config = array();
$config[‘db_hostname’] = “localhost”;
$config[‘db_port’] = “3306”;
$config[‘db_username’] = “yourDBusername”;
$config[‘db_password’] = “yourDBpassword”;
$config[‘db_name’] = “yourDBname”; ?>

Replace the database details with your own above, then make global.php exactly as below:

<?php function db_connect() { global $config; mysql_connect($config[‘db_hostname’].”:”.$config[‘db_port’], $config[‘db_username’], $config[‘db_password’]) or die(mysql_error()); mysql_select_db($config[‘db_name’]) or die(mysql_error()); } ?>

Finally, this huge block powers the statistics:

<p>
<?php require(“../config.php”); require(“../global.php”); db_connect();
$cCount = mysql_query(“SELECT COUNT(copyid) FROM biblio_copy”) or die(mysql_error());
$cCountRes = mysql_fetch_row($cCount);

$cPrice = mysql_query(“SELECT SUM(field_data) FROM biblio_field WHERE tag = ‘541’”) or die(mysql_error());
$cPriceRes = mysql_fetch_row($cPrice);

$cPages = mysql_query(“SELECT SUM(field_data) FROM biblio_field WHERE tag = ‘300’ AND subfield_cd = ‘a'”);
$cPagesRes = mysql_fetch_row($cPages);

$cNonFic = mysql_query(“SELECT COUNT(bibid) FROM biblio WHERE collection_cd = ‘2’”);
$cNonFicRes = mysql_fetch_row($cNonFic);

$cFic = mysql_query(“SELECT COUNT(bibid) FROM biblio WHERE collection_cd = ‘1’”);
$cFicRes = mysql_fetch_row($cFic);

$cDVDs = mysql_query(“SELECT COUNT(bibid) FROM biblio WHERE collection_cd = ’12′”);
$cDVDsRes = mysql_fetch_row($cDVDs);

$cOut = mysql_query(“SELECT COUNT(status_cd) FROM biblio_copy WHERE status_cd = ‘out'”) or die(mysql_error());
$cOutRes = mysql_fetch_row($cOut);

$cIn = mysql_query(“SELECT COUNT(status_cd) FROM biblio_copy WHERE status_cd = ‘in'”) or die(mysql_error());
$cInRes = mysql_fetch_row($cIn);

$circOuts = mysql_query(“SELECT COUNT(copyid) FROM biblio_status_hist WHERE status_cd = ‘out'”) or die(mysql_error());
$circOutsRes = mysql_fetch_row($circOuts);

$circRens = mysql_query(“SELECT COUNT(copyid) FROM biblio_status_hist WHERE status_cd = ‘crt'”) or die(mysql_error());
$circRensRes = mysql_fetch_row($circRens);

$circTotal = mysql_query(“SELECT COUNT(copyid) FROM biblio_status_hist”) or die(mysql_error());
$circTotalRes = mysql_fetch_row($circTotal);

$pTotal = mysql_query(“SELECT COUNT(mbrid) FROM member”) or die(mysql_error());
$pTotalRes = mysql_fetch_row($pTotal);

$pTotalAdults = mysql_query(“SELECT COUNT(classification) FROM member WHERE classification = ‘1’”) or die(mysql_error());
$pTotalAdultsRes = mysql_fetch_row($pTotalAdults);

$pTotalChildren = mysql_query(“SELECT COUNT(classification) FROM member WHERE classification = ‘2’”) or die(mysql_error());
$pTotalChildrenRes = mysql_fetch_row($pTotalChildren);

$pPhones = mysql_query(“SELECT COUNT(mbrid) FROM member WHERE home_phone != ””) or die(mysql_error());
$pPhonesRes = mysql_fetch_row($pPhones);

$pEmails = mysql_query(“SELECT COUNT(mbrid) FROM member WHERE email != ””) or die(mysql_error());
$pEmailsRes = mysql_fetch_row($pEmails);

$cAllBooks = ($cFicRes[0]+$cNonFicRes[0]);

echo “<strong>Live Statistics:</strong><br />

The Thripp Public Library has <strong>$pTotalRes[0]</strong> patrons: <strong>$pTotalAdultsRes[0]</strong> adults and <strong>$pTotalChildrenRes[0]</strong> children.<br />

There are <strong>”.$cCountRes[0].”</strong> items: <strong>”.$cNonFicRes[0].”</strong> nonfiction books, <strong>”.$cFicRes[0].”</strong> fiction books, and <strong>”.$cDVDsRes[0].”</strong> DVDs.<br />

Stats: Nonfiction books: <strong>”; printf (“%01.2f”,(($cNonFicRes[0]/$cCountRes[0])*100)); echo “%</strong>; Fiction books: <strong>”; printf (“%01.2f”,(($cFicRes[0]/$cCountRes[0])*100)); echo “%</strong>; DVDs: <strong>”; printf (“%01.2f”,(($cDVDsRes[0]/$cCountRes[0])*100)); echo “%</strong>.<br />

<strong>”; if ($cOutRes[0] == ‘1’) echo “1</strong> item is”; else echo “$cOutRes[0]</strong> items are”; echo ” checked out and <strong>”.$cInRes[0].”</strong> are on shelf.<br />

<strong>”; printf (“%01.2f”,(($cOutRes[0]/$cCountRes[0])*100)); echo “%</strong> of the catalog is checked out. The average patron has <strong>”; printf (“%01.2f”,($cOutRes[0]/$pTotalRes[0])); echo “</strong> items out.<br />

There are <strong>”; printf (“%01.2f”,($cCountRes[0]/$pTotalRes[0])); echo “</strong> items for every <strong>1</strong> patron.<br />

The collection is worth <strong>$$cPriceRes[0]</strong>, or about <strong>$”; printf (“%01.2f”,($cPriceRes[0]/$cCountRes[0])); echo “</strong> per item.<br />

There have been <strong>$circOutsRes[0]</strong> checkouts and <strong>$circRensRes[0]</strong> renewals; a total of <strong>$circTotalRes[0]</strong>.<br />

&nbsp;&nbsp;&nbsp; This is an average of <strong>”; printf (“%01.2f”,($circTotalRes[0]/$pTotalRes[0])); echo “</strong> per patron, or <strong>”; printf (“%01.2f”,($circTotalRes[0]/$cCountRes[0])); echo “</strong> per item.<br />

There are <strong>”.$cAllBooks.”</strong> books with <strong>”. number_format($cPagesRes[0]).”</strong> pages. The average book has <strong>”; printf (“%01.2f”,($cPagesRes[0]/($cFicRes[0]+$cNonFicRes[0]))); echo “</strong> pages.<br />

The ratio of fiction to nonfiction books is <strong>”; printf (“%01.2f”,(($cFicRes[0]/$cNonFicRes[0])*100)); echo “%</strong>.<br />

The collection represents <strong>$”; printf (“%01.2f”,($cPriceRes[0]/$pTotalRes[0])); echo “</strong> of value per patron.<br />

<strong>”; printf (“%01.2f”,(($pPhonesRes[0]/$pTotalRes[0])*100)); echo “%</strong> of my patrons have telephones and <strong>”; printf (“%01.2f”,(($pEmailsRes[0]/$pTotalRes[0])*100)); echo “%</strong> have email accounts.”;

?>
</p>

I do know this is all over the place, it’s a mess, it’s inefficient, and it probably should all be cached. It’s working great, so I’ll cross the “I have to fix this now!” bridge when I come to it. Feeding the library address into this speed test, it’s 0.4 seconds; the same as thripp.com which is totally cached. That might slow down as the database gets bigger, but for now it’s fine.

You can use this code for your OpenBiblio site, by adding it to opac/index.php, but you have to change some stuff. Notice “collection_cd” and “classification”? The arguments there are hard-coded for my database, so change them for yours. Otherwise, you have to make sure to enter the cost for each item, and the number of pages as “###” or “### pages” in “Physical description (Extent):” and nothing else. If you do this, it’s cool because you get the total number of pages in your library.

Right now, the stats look like this:

Live Statistics:
The Thripp Public Library has 5 patrons: 3 adults and 2 children.
There are 81 items: 63 nonfiction books, 15 fiction books, and 3 DVDs.
Stats: Nonfiction books: 77.78%; Fiction books: 18.52%; DVDs: 3.70%.
2 items are checked out and 79 are on shelf.
2.47% of the catalog is checked out. The average patron has 0.40 items out.
There are 16.20 items for every 1 patron.
The collection is worth $1681.82, or about $20.76 per item.
There have been 19 checkouts and 15 renewals; a total of 34.
    This is an average of 6.80 per patron, or 0.42 per item.
There are 78 books with 29,786 pages. The average book has 381.87 pages.
The ratio of fiction to nonfiction books is 23.81%.
The collection represents $336.36 of value per patron.
100.00% of my patrons have telephones and 100.00% have email accounts.

It’s amazing what computers can do, no? In the print age, or even in an out-of-the-box ILS, it would take hours to compile this report, and you’d have to do it every time something changed. If anyone made reports like this, it would be once a month at best, and still it would be a great drudgery and expense. Not so anymore.

The stats are a bit messed up when you have records you’ve just imported from the Library of Congress, but not processed. Nothing fatal; the numbers are just wrong. Once you’ve done all the cataloging, it’s fine, though.

A good OPAC needs good URLs. My URLs are like lib.thripp.com/93. No titles in the URLs keeps them nice and simple-to-implement. I’m using Linux + Apache, so fixing this is easy. Let me show you my .htaccess file:


RewriteEngine On
RewriteBase /
RewriteRule ^([0-9]+)$ shared/biblio_view.php?bibid=$1&tab=opac [NC]
RewriteRule ^e/([0-9]+)$ shared/biblio_view.php?bibid=$1&tab=cataloging [NC]
RewriteRule ^$ opac/index.php [L,NC]
RewriteRule ^search-results shared/biblio_search.php [L,NC]
RewriteCond %{HTTP_HOST} www.lib.thripp.com$ [NC]
RewriteRule ^(.*)$ http://lib.thripp.com/$1 [L,R=301]

That does all the magic. Change http://lib.thripp.com, of course. This requires changes in opac/index.php and shared/biblio_search.php to match. If you’ve used my code in this article, it’s already done, though. Now, you can jump from a regular view to the view with the edit links by adding “e/” before the bibid. And the OPAC is mapped to the root instead of home/index.php, which is cluttered and has a lot of staff functions. I don’t know why it’s the default home page. Anyway, after the change you can still get there by URL by adding “/home” to your URL. That’s the best way, because not having a link to the staff area from the home page is a bit of security through obscurity.

I’m happy to have started my library, despite being just for family and friends for now. I need to buy a house or a warehouse to host it at, and then open it up to the world. OpenBiblio has good facilities for checkouts, renewals, limits, fines, and even receipt printing, and I feel I can build upon them through my own programming, so on the tech site I’m ready. I plan to have a lending library with a limit of 5 items out per person at a time. You can renew and place holds, but only by phone or by coming in (this is OpenBiblio’s limitation; other OPACs have these features). You get three weeks on everything, except DVDs which are one week. Late fees are 15 cents per item per day.

It might be 5 years before I’m making enough money from advertising on this website to find a space to open the library. I’m not too worried. It’s better to start now than to start later. Here’s a photo of the stacks now:

The Thripp Library shelves

These books displaced my photos, but it’s worth it. I used to have stacks of photos on these shelves, but I crammed them in with other photos on my other bookshelf to make way for the library. If you’re a good friend, feel free to come over to my house, get a library card, and check something out. Make a donation to get this off the ground. If you make a donation, you’re a good friend.

I’m seeing a big gap between what public libraries are… and what they could be. There are no charismatic leaders in librarianship. Most everyone is dull and unoriginal; even the software and basics like online catalogs need lots of work. This is because most libraries are government-funded; even many academic branches are not immune. So the strategy is “let’s waste as much money as possible and leech from the taxpayers,” not “let’s work efficiently and make a real contribution to the community.” More frighteningly, libraries are becoming elitist, discarding old, unpopular, or “offensive” books and rejecting self-published books or anything without an ISBN number. I’ve written a mission statement for the Thripp Public Library to address this:

The Thripp Public Library is founded on a healthy attitude of dissension and skepticism, a distaste for lies and fallacy, and a love of learning from history. To know history and avoid 1984-style revisionism, it’s important to keep old books around. Unfortunately the Volusia County library system doesn’t do this, as I’ve gotten many of my library’s gems right from their book sales. These are my library’s universal principles:

1. Timelessness eschews popularity.
2. The message trumps the medium.
3. Truth is independent of source.

This means that good information can come from any person or organization in any form, be it a book, magazine, CD, DVD, website, etc. I’ve founded the library on timelessness, meaning that I refuse to destroy parts of the collection that are rarely looked at, because they are often the most important. Popular movies circulate more, but are fleeting and unscholarly. I’ll include them if they’re cheap and terribly entertaining, though.

Here’s an example item, with the Thripp barcode:

Thomas Jefferson book

And here’s what a Thripp library card looks like:

Richard X. Thripp's Thripp library card

Cataloging is on hold till I go to the store, because I’ve run out of clear packing tape to affix barcodes and spine labels.

Take a look at the catalog. Just click Search! to see everything. Then come back and tell me you’re not impressed. :wink2:

The Perils of Redundant Linking

Sometimes I’ll write a post, and I’ll mention something twice. Often it’s my wonderful camera, a Canon Rebel XTi. And then I wonder: should I make the text a link twice? In the Rebel XTi case, it’s a link to Amazon.com (an evil affiliate link). Sometimes, the link will be with different text, or in an entirely different context than the first, though it goes to the same page. That could be linking to Glass Drops once while talking about night photography, and then again when discussing raindrops, in the same article.

I’ve noticed other people doing this, and I’m finding it ever more annoying. I’ve found there are two approaches to double-linking:

1. Link redundantly, because your readers will be annoyed and confused that your talking about a subject so much but not linking to it, if they missed the first link. Or do it to really get people to click your affiliate link. More commonly, readers scan your content rather than reading closely. Either they scan by default, or find your writing useless. To accommodate that group, you have to mention important stuff as many times as possible and hope it won’t be missed. Your writing for the unengaged rather than the engaged, and your putting the wrong ones first.

2. Don’t link redundantly, because you choose to cater to thorough rather than casual readers. Readers who take in every word and click every link in perfect succession. Readers who will be annoyed and confused if they find the same page twice in their tab bar after a work-out session with the scroll button. I click every link if I like what I’m reading, because I know the author will have good recommendations about the topic. And I’ve been finding it quite disconcerting when a good author is type 1.

I’ve decided to abandon double-linking because my audience should be type 2 rather than type 1. This goes along with blogging selectively and writing insightful, comprehensive articles rather than shallow, fleeting posts. Stuff like Intrinsic vs. Extrinsic Value rather than a Quick Post on HDR. Stuff that’s unbelievably useful rather than unbelievably useless. Gold rather than garbage.

I don’t mind redundant links outside of a single post. Even if the same links are in the categories list or header, it’s okay, because that content is generally fixed and largely ignored. A contextual link going to the same place is fine, because it adds to the content of the article, unlike saying “check the sidebar.” The problem also is that the header and sidebar are expensive places, because they show up on every page. I can’t be sure how long a link will stay there. If its value is transient it can’t stay up once the value is gone. There is no space for clutter. A services link is still worthwhile now, but in three months I may stop taking commissions entirely, and “check the header for services” would be out of date. The other problem is that it’s better to show than to tell. A direct link is always better than saying “see here” or “search Google.”

Within a single post, Type 1 linking just doesn’t bode with this style at all. It’s fine if you don’t value your readers’ time and assume they don’t read what you write. If you assume no one’s reading, it will come true, because you’ll start writing stuff that’s valuable to no one. But if you’re writing for the heart first, and profit second, it just doesn’t work. Type 2 is the only way to go.

Th8.us: URL trimming service

I created a URL trimming service at Th8.us. The URLs are shorter than Tinyurl: 19 characters instead of 25, in the format http://xxxxx.th8.us. I put it together using Hidayet Dogan’s Phurl for my Twitter account, but then thought it should be released to the world. I made modifications to the code so the random part is a virtual subdomain, is five characters instead of six (it’ll be a while before the 24 million combinations are used up), it respects trailing slashes, it links to the new address instead of just showing it in plain text, and it tells you the number of characters you’ve saved (it’ll even be negative if the original address was shorter). Also, hard-to-read characters are excluded (0, 1, j, l, u, and v). The service is simple, fast, and clean, unlike Thripp.com, which is heavy and feature-laden (flexible) and thus more prone to outages. th8.us shares no code and uses a separate database from Thripp.com, so you can count on the URLs working forever (barring problems with my host, SYN Hosting).

Here’s the first trimmed URL as an example: http://oorph.th8.us/. :cool:

Drag the one-click Twitter bookmarklet from the home page to your bookmarks toolbar. It’ll take you to Twitter and type in the shortened URL of the page you were visiting into the post box.

My photos on the HTTP 500 Internal Server error page

I made a custom Internal Server Error page for the Thripp.com network, with all the photos from my portfolio. Error pages are fun again. :smile:

I put a lot of ads there too, so I can monetize the outages. Plus, the links are to the photos on my jpgmag.com gallery, and all the thumbnails are on Photobucket, so the page is light-weight, won’t chew up bandwidth, and makes my photos accessible when the database is down. It’s only 10KB!

I posted this to digg too:

Check out this HTTP 500 page; instead of being rudely interrupted with a cryptic error message and an accusation that you broke the server, on Thripp.com, you’re greeted by beautiful photography (and plenty of ads to boot).

Enjoy. :cool:

Switched to SYN Hosting, Outage is Over

Hi everyone. The website’s been down for the last 18 hours, since 7:30 A.M. (EDT) this morning, but I’m back now. I discovered it when I awoke at 2 P.M. (I’m happily unemployed), and immediately began trouble-shooting. It wasn’t on my end at all; it had to be Netfirms’ fault (they’ve given me trouble before). Netfirms wasn’t serving up anything from the MySQL database, which cripples me, because this blog is all dynamic.

Netfirms has been growing progressively worse in the past two weeks… FTP has been terribly slow, the website is slow, it’s gone down a couple of times because of them, etc. I called them… and after 30 minutes on hold, hearing only an automated message telling me how “extremely important” I am, I just hung up. By then, it was 3:30, and I decided to give up and switch web hosts. Even though I have Netfirms’ first-year $10 special ending on August 2, I can’t stand it anymore. I did an hour of research, and picked SYN Hosting because they sound good and honest. I sent in the request for an account, and then headed for school (my night class was from 5:30 to 9 P.M.), not being able to do anything more for the time. I had a test in precalculus algebra, and I did poorly on it (will find out Monday). If I do well on Wednesday’s final (2008-06-25), the grade is dropped, so that’s what I need to do now. I should be fine with 85% on the final.

So when I got back home, I got my email from SYN Hosting. I’d already started downloading the files from Netfirms before leaving, and it was done. I promptly switched DNS servers in my triple.com control panel and began uploading files to SYN Hosting. Still haven’t done everything (the stock photos are ~160MB and will take hours). But the site’s back.

It takes a long time on my slow ADSL connection with just 128kb upstream bandwidth. Especially when you have 3000 small files, like with my WordPress MU installation and army of plugins. And I had some trouble importing the MySQL database, since it is so large (23MB). I got it all worked out finally. I’m glad to be back, and sorry for the trouble.

All of the thripp.com network was down. I’m posting this here, because I get 60% of the community’s traffic.

SYN charges $8.34 a month for their basic plan, billed every six months. I searched first, and found the SAVEME offer. Basically, if you’ve had your domain for over six months and are hosted elsewhere, you get three months free. I sent this emphatic message in the notes field when I registered:

Save me! I’m using Netfirms now, and after four outages in the past two days (one right now), and no one to answer their phones, I’ve had it, even though I have a over a month left on my contract. SYN Hosting is a lot better, I can tell.

Boy, was I surprised to log on and see that I’d been given six months free. Here’s what my invoice looks like:

SYN hosting paid

Mind you, I haven’t actually paid them anything. I was expecting to get an email to do so, or at least provide credit card or PayPal billing information for their security, but no. You don’t often see this kind of commitment from hosting companies.

What I’m really enjoying, is the fast loading times. Checking on this website speed test, I see my page takes under a second to be compiled:

thripp.com loads quickly

In my last days with Netfirms, it was often over 4 seconds. Waiting isn’t fun.

Netfirms claims to give me something ridiculous like 2TB of disk space and 2000TB of bandwidth. SYN Hosting keeps it real: 6GB disk space and 120GB bandwidth each month. And their interface and control panels are better. You can even see how much CPU and RAM resources you’re using. That’s far more important than bandwidth, because with a dynamic, database-powered site, bandwidth isn’t what drags you down.

Managing your own website is hard work. I’m glad now I can stop worrying about it disappearing. Go ahead and try out SYN Hosting; they’re a real gem. Make sure to enter the coupon “saveme” and tell them who you’re transferring from in the notes, if you want a few months free. I’m looking forward to much more enjoyable days here, for my readers/viewers and me.

The Profit Police and How They Kill Everyone

Silhouette of a man holding a hammer -- Photography by Richard X. Thripp

The profit police are as old as eternity, but insidious as the devil. They threaten to steal our happiness, to sour us with envy, hatred, and guilt. Their orthodoxy is codified in institutional policies all over the world. They kill everyone. They are us.

Profit is not just money. Profit is also prestige, notoriety, and mere exposure. The profit police take keeping up with the Joneses to the extreme. They tell us that promoting our names or starting a business is selfish, greedy, and wrong. They are responsible for the professionalization of jobs that have no business being bureaucratized. They create sad terms like vanity press, as though not having a book approved by a committee makes the author an egotistical lunatic. Their influence starts with us, at the micro level.

The Junior Anti-Profit League is alive and well on the forums of the Internet. Well-meaning adults persist with policies of “no advertising, no self-promotion, no links to your website, no ‘commercialism.'” They cry foul at affiliate links, for no reason further than to stifle the success of their users (my photography articles are proudly littered with them). Brilliant computer-programmers publish free software with the clause, “no commercial use,” as if every dollar earned with the help of their applications comes straight from their wallets. As if profit is bad. As if the very act of seeking prosperity—called the American Dream by many—is the bane of humanity. Run the phrase, “free for non-commercial use” through Google, and you get 264,000 web pages, all of people afraid of something.

What are they afraid of? The success of others. Why are they afraid of it? Because they perceive that it diminishes themselves. We all do this. Charles Wheelan, financial blogger, elaborates:

“There’s a very interesting strain of economic research showing that our sense of well-being is determined more by our relative wealth than by our absolute wealth.

In other words, we care less about how much money we have than we do about how much money we have relative to everyone else. In a fascinating survey, Cornell economist Robert Frank found that a majority of Americans would prefer to earn $100,000 while everyone else earns $85,000, rather than earning $110,000 while everyone else earns $200,000.

Think about it: People would prefer to have less stuff, as long as they have more stuff than the neighbors.”

This scales down to the minute level. I am guilty of it myself. When I opened my website, I set my Google AdSense advertising up to filter ads for other photographers. I stopped doing this after a week, realizing how silly it is. But the fact is that fear of the success of others is a subconscious human response. It’s also irrational. Another’s persons success is not my loss, no matter how it may seem.

I’ve had my own encounter with The Profit Police as of yesterday. If you’ve read The Thievery of richardxthripp, you know of my rush to secure my name on the popular blogging, photography, and social networking websites after richardxthripp.blogspot.com was claimed by spammers. One of the sites I registered for was 43 Things, a destination for sharing your life’s goals with the world. I’ve admired their community for a while, so I added to the discussion to help others with two things I’ve done, and to drive visitors to my website:

I added to the goal, have a blog:

“I’ve done this now. Set up my blog for my photography: Brilliant Photography by Richard X. Thripp. Started three months ago, but it’s an ongoing project. I’m using WordPress as my blogging software; it’s worth it to have your own domain name so you aren’t tied to any third party.”

And, sharing my knowledge on playing the piano:

“Playing the piano is a great hobby for reflection, mental and finger dexterity, appreciating music, and enjoying with others. I’ve been playing since ten; here’s a performance from January.

What I don’t buy, is that you have to start when you’re young. Plenty of adults learn to type quickly (with our newfound reliance on computers), yet that takes dexterity, skill, and practice, like piano. And also—you can look at your fingers while playing. You may come to memorize a song just from working on it a lot, and then begin watching your fingers so you don’t miss the keys; don’t fight it.”

Don’t bother looking for my entries; they’ve been vaporized now, along with my page. I’m alive in the Google cache for now: have a blog, play the piano (2008-07-31 Update: now removed). Little did I read that they have a policy against my kind of writing:

“43 Things is for personal use only. If you sell or promote products, services or yourself through your 43 Things page, we will suspend your account.”

So promoting yourself is not personal use? Sure, if you own a social network you can enact whatever rules they want, but that doesn’t mean you should. This is the cowardly, suicidal behavior that profit policing drives us to. It is cowardly because it sweeps under the rug the work of others, as if the publisher deserves no credit for his insights. It is suicidal because it destroys discussions and useful information at the fear of others’ gain, reducing morale and alienating users.

I used to release my stock photographs with a license that said “no commercial use.” It took me months to finally give it up. The question: What if someone gets rich from using the resources I provide? They’d be earning money off my hard work! The answer: So what? This is not a Reversi game, where every acquisition by your opponent is an equal blow to you. 200 A’s do not necessitate 200 F’s. Life is not a zero-sum game. It’s time we stopped playing it as one.

Recommended reading:
How Jealousy and Envy Destroy Happiness by Steve Olson
Life ain’t a zero-sum game.
Why Income Inequality Matters by Charles Wheelan