Force Directed Positioning with PHP, SVG, and HTML
I recently released a proof-of-concept library for drawing force directed graphs with PHP. Right now, it is absurdly simple and has a few quirks, but it gets the job done. It's original purpose is for visualizing search results within a controlled vocabulary, although I can imagine other uses.
Right now, it will only properly function in browers that support the <canvas> tag, although that will change in a couple of weeks. I also hope to implement a few parsers to make it easier to create graphs (I'm thinking RDF and dot will be sufficient). There's also some necessary performance enhancements before it can be considered production-ready.
You can download the files here or see the library in context at the bvault repository on launchpad.
This work is inspired by the JSViz library which renders forced directed graphs on the client side using javascript.
Fedora, GSearch, Solr [UPDATED]
I am at the Fedora Commons Red Island Repository Institute this week to learn more about the Fedora Commons repository to help in my digital repository work at WGBH. For the last week, or so, I have been struggling with getting Fedora and Solr to play nice with each other, so we can do really interesting searches, faceted browsing, and more.
The secret, I have discovered, is to start with a pre-populated solr index (I don't know if this is strictly necessary, but it solved one of our major errors, and to run solr from its included Jetty engine (again, possibly not necessary, but I don't have the Java application experience to delve into complex configurations).
[UPDATE]
Here are some step-by-step directions to get GSearch and Fedora to play nice together:
###gsearch### 1) Download GSearch 2.1.1 and copy the fedoragsearch.war file to $TOMCAT_HOME/webapps 2) Restart tomcat to unpack WAR 3) configvalues.xml : Update soap.deploy.hostport, .user, and .pass :29,31s/basic/solr/g Line 242: set solr.index.1.indexbase and .indexdir 4) ant -f configvalues.xml configOnWebServer 5) cd $TOMCAT_HOME/webapps/fedoragsearch/WEB-INF/classes/ 6) cp -R configBase/updater configDemoOnSolr 7) configDemoOnSolr/fedoragsearch.properties append: fedoragsearch.updaterNames = BasicUpdaters 8) Edit configDemoOnSolr/index/DemoOnSolr/demoFoxmlToSolr.xml as appropriate. Copy this file to config/index/DemoOnSolr. ###SOLR### 9) Download solr 1.2 and unpack to $FEDORA_HOME/solr-1.2 10) mkdir $FEDORA_HOME/solr 11) cp -R solr-1.2/example/solr/* $FEDORA_HOME/solr 12) cp solr-1.2/dist/apache-solr-1.2.0.war $FEDORA_HOME/solr/solr.war 13) Edit conf/schema.xml to reflect the schema choices you made in demoFoxmlToSolr.xml 14) Create $TOMCAT_HOME/conf/Catalina/localhost/solr.xml 15) Restart Tomcat to start solr 16) Use solr to initialize the indexes a) Create a compatible solr ingest xml file by running one of your foxml files through your demoFoxmlToSolr.xslt file (maybe not necessary?) e.g. : xsltproc -o $FEDORA_HOME/tmp.xml tomcat/webapps/fedoragsearch/WEB-INF/classes/config/index/DemoOnSolr/demoFoxmlToSolr.xslt data/objects/2008/0808/14/47/wgbh_100 b) cp $FEDORA_HOME/solr-1.2/example/exampledocs/post.sh $FEDORA_HOME/solr c) Edit post.sh to change the URL (e.g. URL=http://localhost:8080/solr/update) d) ./post.sh $FEDORA_HOME/tmp.xml && rm $FEDORA_HOME/tmp.xml ###DOES IT WORK?### 17) Go to http://localhost:8080/fedoragsearch/rest -> browseIndex; Does your object exist? 18) Check gsearch -> solr integration works [updateIndex fromPid] 19) Import your current foxml files with [updateIndex fromFoxmlFiles]
This still doesn't take advantage of the JMS capabilities of Fedora 3.0, unfortunately; that's the next challenge.
Zend_Db_Adapter_Mysqli and PHP 5.3
If you’re using PHP 5.3 and Zend_Db with MySQL, you might see
Fatal error: Uncaught exception 'Zend_Db_Statement_Mysqli_Exception' with message 'Mysqli statement execute error : No data supplied for parameters in prepared statement' in [...]/trunk/library/Zend/Db/Statement/Mysqli.php:215 Stack trace: #0 [...]/trunk/library/Zend/Db/Statement.php(283): Zend_Db_Statement_Mysqli->_execute(Array) #1 [...]/trunk/library/Zend/Db/Adapter/Abstract.php(435): Zend_Db_Statement->execute(Array) #2 [...]/trunk/library/Zend/Db/Adapter/Abstract.php(513): Zend_Db_Adapter_Abstract->query('INSERT INTO `us...',Array) #3
Turns out this is a known bug (ZF-2529) with a proposed patch that hasn’t made it into a release yet. How annoying for earlier adopters who want to take advantage of PHP namespaces (which is really helpful for making generic code..)
Zend Framework 1.6: Zend_Paginator
The new Zend Framework 1.6 release candidate includes a Zend_Paginator class, which is an excellent thing to have around because I know I’ve re-invented that wheel on every site I’ve developed. My only criticism of the new Zend_Paginator is it offers a daunting amount of possibilities.
To that end, here’s a quick cheat sheet to get things up and running:
First, we need to establish the data source and initialize the paginator
class EventController { public function listAction() { //get an array of events $arrEvents = Core::Event::fetch_by_filter(array('link' => array('child' => $objCharacter), 'eventExpire' => time())); //initialize the paginator using the handy factory method.. $paginator = Zend_Paginator::factory($arrEvents); //tell the paginator which page we're on $paginator->setCurrentPageNumber($this->_getParam('page')); //pass the paginator into the view $this->view->paginator = $paginator; } }
Then we need to put the paginator to use:
< ?php if (count($this->paginator)): ?>
-
< ?php foreach ($this->paginator as $item): ?>
-
< ?php echo $item->title; ?>
< ?php echo $item->message ?>
< ?php endforeach; ?>
Then, we need to create the pagination control (surely this could be a view helper?). The manual page for Zend_Paginator provides a number of different examples. I’ve chosen to use the sliding control just for aesthetic purposes:
< ?php if ($this->pageCount): ?>< ?= $this->firstItemNumber; ?> - < ?= $this->lastItemNumber; ?> of < ?= $this->totalItemCount; ?> <a href="< ?= $this->url(array('page' => $this->first)); ?>">First</a> | < ?php if (isset($this->previous)): ?> <a href="< ?= $this->url(array('page' => $this->previous)); ?>">< Previous</a> | < ?php else: ?> <span class="disabled">< Previous</span> | < ?php endif; ?> < ?php if (isset($this->next)): ?> <a href="< ?= $this->url(array('page' => $this->next)); ?>">Next ></a> | < ?php else: ?> <span class="disabled">Next ></span> | < ?php endif; ?> <a href="< ?= $this->url(array('page' => $this->last)); ?>">Last</a>< ?php endif; ?>
Finally, a quick little Zend_Router route to pretty up the URL:
$router->addRoute('event_list', new Zend_Controller_Router_Route('event/list/:page', array('controller' => 'event', 'action' => 'list')));
Now, when you navigate to /event/list/1, you have a paginated list (unstyled):
Zend_View and UTF-8
On one of my projects, we read in an RSS feed from Wordpress, using Zend_Feed:
$feed = Zend_Feed::import('http://dormae-online.com/blog/?feed=rss2'); foreach($feed as $entry) { echo '<h2>' . $entry->title . '</h2>'; echo '<div>' . $entry->author . '</h2>'; echo '<div>' . $entry->pubDate . '</div>'; echo '<div>' . $entry->{'content:encoded'} . '</div>'; }
This is a wonderful system that allows us to take advantage of the content management features that Wordpress offers, while still presenting a consistent and simple interface for the visitor. There’s just one minor problem: Wordpress uses UTF-8 encoding, while Zend Framework defaults to Latin-1, causing some minor character flaws when presented things like ü or ä. To solve this, we tell Zend_View (and the browser) exactly which character coding to expect:
$body = new Zend_View(); $body->setEncoding('UTF-8'); $body->headMeta()->appendHttpEquiv('Content-Type', 'text/html; charset=UTF-8');
Does anyone know how to tell Zend to use UTF-8 by default?