web development

Contacts Plug-In Update

Another beta version of the Address Book plug–in for RapidWeaver

Here is a new beta of the Contacts plug-in. This release simply corrects a problem running under 10.5 and extends the preview date. No work has been done to enhance the plug-in for use in RapidWeaver 4 or use the new API features.

'List view' will give more reliable results than 'Cards view' but please test thoroughly before using on a public site. Most of the 'display issues' still occur in Internet Explorer 6 and vary depending on the RapidWeaver theme used. Clicking on telephone numbers still gives unpredictable results in Internet Explorer.

Many of the issues with the first version still remain. Public Beta 2 will expire on 25th June 2009.

Download Contacts Public Beta 3.

|

Safari Kiosk Tabs

An AppleScript to ensure that new Safari windows are opened in a tab

In the Watershed Café/Bar there are a number of ‘Slacker Tables’—public web browser kiosks set in glass tables. Oliver asked about a way to ensure that the instances of Safari running on the tables would open a new tab and not a new window. This is a simple AppleScript, to be saved as an Application and run, in a loop in the background.

This was only a quick, five minute hack but the results are surprisingly usable. Running the script as an application in the background doesn't appear to affect performance or stability over longer periods of time.

Note: this script has only been tested with Safari 3.2.1 in MacOS X 10.5.5

  1. repeat
  2. delay 1
  3. tell application "Safari"
  4. if the (count of documents) > 1 then
  5. set theURL to the URL of the front document
  6. if theURL is not "" then
  7. close front document
  8. tell front window
  9. -- Safari appears to only let the front window create tabs
  10. set theTab to make new tab
  11. set current tab to theTab
  12. set the URL of theTab to theURL
  13. end tell
  14. end if
  15. end if
  16. end tell
  17. end repeat
|

Unfuddle

Initial personal responses from using web-based software and project management for a day.

Unfuddle.com provides issue tracking tickets, a documentation wiki, Subversion & Git repositories along with web-based interfaces to all these services. Whilst beginning a new personal iPhone project today, I wanted to test a hosted service to aid with small, non-public development projects.

Sign up was quick and simple; further profile information can be entered at a later date. Next, the user is given the option to create a new repository—a choice of Subversion or Git. I chose Subversion. The repository was available immediately both in local client applications and in the web-based browser. The connection was not the fastest, especially for a Sunday afternoon and I wonder how it will cope midweek. As mentioned, I writing a small, by their nature, iPhone application which shouldn't overwhelm any network connections.

The UI is OK. The interface, especially for tickets is not as nice as lighthouseapp.com which we use at Watershed. The web-based source code browser does support Objective-C highlighting. Which was nice.

An HTTP REST API is available with read & write access to most of the data. At Watershed, a Cocoa bundle uses the REST API to allow end users to send feedback from within the application directly into the Lighthouse database. Repository commit comments can be linked against individual bugs with a rudimentary text formatting pattern in the comment.

A dashboard widget is available for MacOS X. The widget is well designed and shows the latest messages, repository commits and project progress. Clearly designed and concise email reports can be received hourly, or at less frequent intervals. I found, as a solo developer, these two features less useful but small teams may benefit.

Unfuddle could save lots of time, configuring a server, providing network access & off-site backup services, adding repositories and writing Subversion hook scripts.

|

New Design

A new, easier to read, design for my personal site.

As much as I liked the last design for the site, it just didn't work anymore. The columns were too narrow: the code samples had to be edited to fit the narrow width and images had to be scaled fairly small to fit the narrow colum.

I designed and almost finished the RapidWeaver template almost year ago. Whilst adding recent posts with copious code examples, I saw that I really needed to get the new template used. I wanted the new design to be easy to read longer articles and typographically mature. I do think the site, is perhaps, not as distinctive, but is far less painful to read.

I'm using RapidWeaver 4.0 now, and have made a few small changes, partly due to new features in RapidWeaver, partly due to changes at Watershed and partly because I just haven't completed the template. Comments are back, iChat status and links are gone and so is Twitter messages.

|

Using RESTful WebObjects

Using requests with the WebObjects REST Handler for HTTP requests.

The previous post described building a new request handler allowing REST behaviour in WebObjects applications. This post will explain how the handler responds to specific requests. The current test application was built by simply, creating a new WebObjects application in Eclipse, adding a business objects framework and adding the REST handler. The test framework is the same framework that provides the foundation for www.watershed.co.uk so the examples will deal with films, screenings, seasons & festivals. The examples below use two Entities from the model, Exhibits and Programmes. Essentially, a Programme represents a season or festival of films. An Exhibit, generally, represents a film. There is a to-many relationship from Programme to Exhibits; i.e. A Programme contains a collection of Exhibits. This can be seen in use at the Watershed site. The reverse relationship is also modelled, so Exhibits have a to-one relationship to a Programme.

exhibit_programme

Rewriting

REST URLs describe the location of the resource in a hierarchy, so arguments are sent mimicking a directory structure: www.watershed.co.uk/cgi-bin/WebObjects/WatershedAPI.woa/REST/exhibits/1527 The request URLs are often written into executable code or synthesised from String components so keeping the URLs simple and short aids their use. URLs generated by the data modules are 'pretty' and incoming requests are rewritten in Apache so the previous URL becomes: api.watershed.co.uk/exhibits/1527.

Tools

Although not particular to our WebObjects REST Handler, using special tools can make testing much more efficient and pleasant. Whilst a web browser can be used to generate GET and POST requests they struggle with the other HTTP methods. Using CURL, from Terminal, can be useful, optionally adding headers to, or, setting the method or body of, the HTTP request. Typing man curl into Terminal will display a page explaining the options. RESTClient is a Java Swing application to test RESTful web services. It can be used to test variety of HTTP communications and has GUI options for most of the HTTP 1.1 specification, including setting the method, authentication values and the message body as well as viewing the response headers. RESTClient is a useful tool for any type of web application development.

REST URLs

URL Structure

Just as a WebObjects' component action URL has a structure, then URLs accepted by the REST Handler have a structure. Each action has a target and a method. The method is part of the HTTP specification and is explained later. The URL structure is essentially:

host/entity_name/primary_key/relationship/...

The entity name and the primary key together define the target. The entity names in the URLs follow a casual, but established, convention and are lower-case, underscore-separated and plural. So the URL /exhibits/1527 will map to an Exhibit Enterprise Object with a primary key value of 1527. Using the model described earlier, we can find the Programme with the primary key of 57 by using the URL: api.watershed.co.uk/programmes/57/ . As the entity name is underscore separated then to set the target for the MediaSet entity with a primary key of 1234, we can use the URL: api.watershed.co.uk/media_sets/1234.

The REST Handler uses the EOModels in the application, or linked frameworks, to map the URL to the correct Entity names and thence to a table in the database. The Handler then retreives the correct Enterprise Objects and delivers these targets to the appropriate data module for output in the response.

Relationships & Key paths

Objects in isolation are often of little practical use. Modelling real world environments requires a hierarchy of objects and containers. Creating a new season isn't useful unless we can insert films into it.

Once the target object has been established key paths defined in the model are then available. The relationship between Exhibit and Programme, illustrated in the diagram above, can now be used. To find the Programme of an Exhibit we use the URL: api.watershed.co.uk/exhibits/1527/programme/. We can follow the key path, defined in the EOModel, even further by using the to-many relationship back to an array of Exhibits: api.watershed.co.uk/exhibits/1527/programme/exhibits/. In a similar fashion to entity names, key paths in the model are renamed to maintain the pretty URLs. Although not illustrated, Exhibit has a to-many relationship to an array of MediaSets, named mediaSets. Using this relationship results in a URL similar to: api.watershed.co.uk/exhibits/1527/media_sets/.

Now that we have URLs that point to relationships, we can use these to add objects into and remove objects from collections.

Model Delegates

Comparing the illustration of the Entities, above, and the actual XML generated by a GET request displays an inconsistency. Although, attributes with null values are not currently listed, the attributes notes and history are never included. The REST Handler and the data modules use the model delegate to provide some information about how to render the data. To hide the notes and history attributes:

  1. public NSArray excludeAttributesForEntity(String entityName) {
  2. // Always remove notes and history regardless of
  3. // the entity
  4. return new NSArray(new String[] {"notes", "history"});
  5. }

The use of the model delegate is optional and if the delegate is not set, the entity and attribute names will be taken from the model and adjusted.

Varying Data Representations

Outlined in the last post was a technique whereby changing the Accept HTTP header changed the response data format or allowed a client to send updated data to the web application in a variety of formats. Sending a request using cURL in Terminal shows the effect of the accept header.

curl -H 'Accept: text/xml' http://api.watershed.co.uk/exhibits/1530/
produces an XML response, whilst:

curl -H 'Accept: text/plist' http://api.watershed.co.uk/exhibits/1530/
produces a response in Apple's Property List format.

Actions

As the URL specifies the resource then the HTTP method specifies the action to perform on it. The REST Handler responds to the HTTP actions defined in the 1.1 specification. Four of the HTTP methods are mapped to the CRUD actions. The currently deployed application will not receive requests other than GET, HEAD & POST. The FreeBSD web server adapter, for watershed.co.uk applications, was compiled from the Apple source code and does not support the PUT & DELETE methods. The sites will be moving to a new virtual server in the next couple of weeks and we will compile a new adapter from the WebObjects 5.4 adapter source.

To read an Exhibit from the data store:
curl -X GET http://api.watershed.co.uk/exhibits/1530/

To delete an Exhibit from the data store:
curl -X DELETE http://api.watershed.co.uk/exhibits/1530/

To update the title attribute of an Exhibit in the data store:
curl -X PUT -d "A new title</exhibit>" http://api.watershed.co.uk/exhibits/1530/</code></p> <p>To <em>create</em> a new Exhibit in the data store:<br /> <code>curl -X POST -d "<exhibit><title>A new Exhibit<title><copyText>What a wonderful…<title></copyText>" http://api.watershed.co.uk/exhibits/1530/</code></p> <p>By following the key paths in the URLs we can insert and remove objects from a relationship. This part is still very much a work in progress and is still not 'quite right'.</p> <p>To insert a new Exhibit in the data store and add it to the array of exhibits in a programme, specify the array as the target resource:<br /> <code>curl -X POST -d "<exhibit><title>A new Exhibit<title><copyText>What a wonderful…<title></copyText>"<br />http://api.watershed.co.uk/programmes/57/exhibits/</code></p> <p>To remove an Exhibit from the to many relationship of a Programme but leave it in the data store:<br /> <code>curl -X DELETE http://api.watershed.co.uk/programmes/57/exhibits/1530/</code></p> <h2>Notes and Incomplete</h2> <p>As mentioned previously, the adapter installed on the web server does not handle PUT & DELETE requests but we will compile a new version that supports these methods shortly. Manipulating relationship collections feels 'clunky' at the moment and this needs some work. The data modules, both the XML and the Property List, need significant work. A JSON data module would be a welcome addition and would compliment the XML and Property List moduels.</p> <p>One of the original reasons for REST Handler was to provide an efficient and quick data flow to a new persistence framework for Cocoa applications. We already have a framework that consumes large chunks of data from a database, we need to deliver data in a constant trickle just as it's needed. Large collections, and objects in a collection, should be available in a more terse output to reduce response times and bandwidth.</p><p class="blog-entry-tags">Tags: <a href="tag-webobjects.html" title="WebObjects" rel="tag">WebObjects</a>, <a href="tag-java.html" title="Java" rel="tag">Java</a>, <a href="tag-xml.html" title="XML" rel="tag">XML</a>, <a href="tag-web-development.html" title="web development" rel="tag">web development</a>, <a href="tag-rest.html" title="REST" rel="tag">REST</a></p><div class="blog-entry-comments"><a class="blog-comment-link" href="javascript:HaloScan('rw_unique_entry_id_42_page0');"><script type="text/javascript">postCount('rw_unique_entry_id_42_page0');</script></a> | <a class="blog-trackback-link" href="javascript:HaloScanTB('rw_unique_entry_id_42=page0');"><script type="text/javascript">postCountTB('rw_unique_entry_id_42_page0'); </script></a></div></div></div> <div id="unique-entry-id-40" class="blog-entry"><h1 class="blog-entry-title"><a href="restful_webobjects.html" class="blog-permalink">RESTful WebObjects</a></h1><div class="blog-entry-date">02/06/08 10:13 Filed in: <span class="blog-entry-category"><a href="category-watershed.html">Watershed</a></span></div><div class="blog-entry-body"><p class="summary">Development of a REST request handler for WebObjects applications.</p> <p>As part of watershed.co.uk's ongoing development, we have a number of immediate requirements:</p> <ul> <li>Start building a replacement for the FBTFPersistentObjects framework used to provide object persistence and network database connectivity for Communicate.</li> <li>Vend XML and other data types using a technology with easier to implement clients than SOAP.</li> <li>Transfer data to iPhone applications efficiently using XML or binary plists.</li> <li>Do all the above across a number of different databases and business object frameworks.</li> </ul> <p>REST web services vending XML & plist data appeared a good fit for all the above but we didn't want to have to write a whole collection of direct actions and components for each site. So we are in the process of building a REST Request Handler for new and existing applications. This is still a work in progress and is only, a day or two old, but hopefully in the near future there should be useable code alongside the explanation.</p> <h2>So how do we use our REST Request Handler in applications?</h2> <p>First we create a new instance of the handler and assign it to handle particular requests. We do this in Application's constructor in Application.java</p> <ol class="code"> <li><code>RESTHandler restRequestHandler = new RESTHandler();</code></li> <li><code>restRequestHandler.setSecurityDelegate(new MyRESTSecurity());</code></li> <li><code>registerRequestHandler(restRequestHandler, RESTHandler.REQUEST_HANDLER_KEY);</code></li> </ol> <p>We also set a security delegate here which is explained below. The request handler key is "REST" so any request sent to www.domain.com/cgi-bin/WebObjects/App.woa/REST/... will be processed by our handler.</p> <p>Unfortunately, the REST Handler is not quite the simple, drop-in unit we would like. WebObjects will only create and initialise a WORequest with a HTTP GET, HEAD & POST method. Sending a HTTP DELETE or PUT action to a WebObjects application results in an exception. The solution is quite simple, subclass WORequest and return it from a re-implemented createRequest(...) in your Application class.</p> <h2>Security Delegate</h2> <p>The REST Handler can automatically provide public access to any database so as a precaution a security delegate must be implemented and added to the request handler before any requests can be accepted. The security delegate can be any object that implements the few methods declared in the security delegate interface, RESTSecurityDelegate.</p> <ol class="code"> <li><code>public interface RESTSecurityDelegate {</code></li> <li class="indent1"><code>public int requiredHTTPAuthentication(WORequest aRequest);</code></li> <li class="indent1"><code>public boolean requireSecureTransport();</code></li> <li class="indent1"><code>public boolean shouldAllowRequest(WORequest aRequest);</code></li> <li><code>}</code></li> </ol> <p>The security delegate methods are simple to implement and can be varied per request. There are some general REST authentication and authorisation practices in use and these leverage existing features of HTTP: with a secure HTTPS connection to prevent eavesdropping and use, either, HTTP Basic or Digest authentication, or, include a security token as a HTTP header key such as:</p> <p><code><pre>GET / HTTP/1.1 User-Agent: curl/7.16.3 (powerpc-apple-darwin9.0) libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3 Host: www.watershed.co.uk X-Watershed-Auth: U2FsdGVkX1zuHyuXFLEBoBjEHtQO. Accept: */*</pre></code></p> <p>Currently only HTTP Basic support is implemented and we will add Digest at a later date. The REST Handler will first check if it is allowed receive requests over plain HTTP by calling requireSecureTransport(). If the return value is true then the handler will redirect the client to a secure connection. Next the REST Handler asks the delegate, using <code>requiredHTTPAuthentication()</code>, if the request will require authentication and, if the client hasn't provided any, will ask the client to provide authentication. Currently, only Basic HTTP authentication is supported. Finally, the delegate decides if the handler should process the request in the <code>shouldAllowRequest(WORequest aRequest)</code> method.</p> <p>The listing below illustrates a possible implementation of the required methods.</p> <ol class="code"> <li><code>public class SecurityDelegate implements RESTSecurityDelegate {</code></li> <li><code></code></li> <li class="indent1"><code>public int requiredHTTPAuthentication(WORequest aRequest) {</code></li> <li class="indent2"><code class="comment">// Give everyone read-only access</code></li> <li class="indent2"><code>if (aRequest.method().equals("GET")) return SecurityDelegate.AUTHENTICATE_NONE;</code></li> <li class="indent2"><code>return SecurityDelegate.AUTHENTICATE_BASIC;</code></li> <li class="indent1"><code>}</code></li> <li><code></code></li> <li class="indent1"><code>public boolean requireSecureTransport() {</code></li> <li class="indent2"><code>return false;</code></li> <li class="indent1"><code>}</code></li> <li><code></code></li> <li class="indent1"><code>public boolean shouldAllowRequest(WORequest aRequest) {</code></li> <li class="indent2"><code class="comment">// Give everyone read-only access</code></li> <li class="indent2"><code>if (aRequest.method().equals("GET")) return true;</code></li> <li><code></code></li> <li class="indent2"><code class="comment">// A user must be authenticated to change resources</code></li> <li class="indent2"><code>if (aRequest.headerForKey("authorization") != null) {</code></li> <li class="indent3"><code>String encodedAuth = aRequest.headerForKey("authorization");</code></li> <li class="indent3"><code>String decodedAuth = null;</code></li> <li class="indent3"><code>BASE64Decoder decoder = new BASE64Decoder();</code></li> <li class="indent3"><code class="comment">//encoded string starts after "Basic " </code></li> <li class="indent3"><code>encodedAuth=encodedAuth.substring(encodedAuth.indexOf(" ")+1); </code></li> <li class="indent3"><code>try{</code></li> <li class="indent4"><code>decodedAuth=new String(decoder.decodeBuffer((new ByteArrayInputStream (encodedAuth.getBytes())))); </code></li> <li class="indent3"><code>} catch(IOException ex) {} </code></li> <li><code></code></li> <li class="indent3"><code>String tokens[] = decodedAuth.split(":");</code></li> <li class="indent3"><code>if(tokens.length != 2) return false;</code></li> <li><code></code></li> <li class="indent3"><code class="comment">// Check the username & password</code></li> <li class="indent3"><code>if (tokens[0].equals("benjamin") && tokens[1].equals("mypassword")) return true;</code></li> <li class="indent2"><code>}</code></li> <li class="indent2"><code>return false;</code></li> <li class="indent1"><code>}</code></li> <li><code></code></li> <li><code>}</code></li> </ol> <h2>Data Modules</h2> <p>Once the security delegate has approved the request, a data module is then invoked with the task of handling of the request. Using a number of installed data modules allow the application to vend REST web services in any number of different data formats. Data modules render Enterprise Objects out in a response and update, or insert, EO's from parsed data in a request. The data module also deletes EO's from the store for some requests. Each data module implements an interface based on HTTP actions and the REST Handler invokes a method passing the original WORequest and the target EO, or array of EO's.</p> <p>Currently, the REST Handler selects an appropriate data module based on the Accept HTTP header sent in the request by the client. It was assumed that clients would have fine grained control over the request and the client would not always be a browser window so the REST Handler treats each acceptable MIME Type as HTTP 1.1 would, MIME Types are listed in order of preference: most desirable first. There is one exception to the HTTP 1.1 specification; the relative quality parameter is currently ignored. If a header such as <code>Accept: image/jpeg, video/mp4, text/xml;q=0.9, text/json, text/plist, */*</code> were received then the request handler would look first for a module that handled JPEG images and so on.</p> <p>Each available data module is mapped to one or more MIME types. Currently we are just using a particular XML data module for testing and in the process of writing a plist module for use with Cocoa desktop and iPhone clients. A module is not required to support all four actions create, read update, and delete. Extra modules, such as JSON or vCard can be written relatively quickly and registered with the REST handler before accepting requests. There currently isn't a way to request a particular data format based upon the 'file' suffix in the request.</p> <ol class="code"> <li><code>restRequestHandler.registerDataModuleForMIMEType(new DataModuleXML(), "*/*");</code></li> </ol> <h2>Model Delegate</h2> <p>The REST Handler will pass the request and the target Enterprise Objects to the data module but on some occasions we may not want make every entity available to the public. We may also want to make some attributes, such as private internal notes, unavailable. When a request needs to render 10,000 objects to XML we may not need to include every attribute, just an essential subset. An optional model delegate provides the REST Handler and the data modules with answers and information they need to handle the requests efficiently. If the model delegate is not provided then the handler use the default model group.</p> <ol class="code"> <li><code>public interface RESTModelDelegate { </code></li> <li class="indent1"><code>public boolean cacheModelAdjustments();</code></li> <li class="indent1"><code>public boolean includeEntity(WORequest aRequest, String entityName);</code></li> <li><code></code></li> <li class="indent1"><code>public NSArray removeAttributesForEntity(String entityName);</code></li> <li class="indent1"><code>public NSArray includeExtraAttributesForEntity(String entityName);</code></li> <li><code></code></li> <li class="indent1"><code>public NSArray attributesForCollection(String entityName);</code></li> <li class="indent1"><code>public NSArray attributesForObject(String entityName);</code></li> <li><code></code></li> <li class="indent1"><code>public String aliasForEntity(String entityName);</code></li> <li><code></code></li> <li class="indent1"><code>public String labelForEntity(String entityName);</code></li> <li class="indent1"><code>public String labelForEntities(String entityName);</code></li> <li class="indent1"><code>public String labelForProperty(String propertyName);</code></li> <li><code>}</code></li> </ol> <h2>To be Implemented…</h2> <p>The REST Handler has only had a handful of train journeys of development so there are plenty of rough edges. It's currently difficult to adjust or influence the target enterprise objects provided to the data modules which would be useful for situations where we may not want to include all the available films, just the ones screening this month. There is no HTTP Digest authentication and the ability to specify query strings, such as <code>commence<2007-05-29</code> or <code>isPublic=true</code>, in the URL or a request header would be very useful. There is still plenty of work to be done until the REST Handler is complete but it will be a valuable tool as we make architectural, storage and communication changes in watershed.co.uk and dshed.net.</p><p class="blog-entry-tags">Tags: <a href="tag-webobjects.html" title="WebObjects" rel="tag">WebObjects</a>, <a href="tag-web-development.html" title="web development" rel="tag">web development</a>, <a href="tag-java.html" title="Java" rel="tag">Java</a>, <a href="tag-xml.html" title="XML" rel="tag">XML</a>, <a href="tag-rest.html" title="REST" rel="tag">REST</a></p><div class="blog-entry-comments"><a class="blog-comment-link" href="javascript:HaloScan('rw_unique_entry_id_40_page0');"><script type="text/javascript">postCount('rw_unique_entry_id_40_page0');</script></a> | <a class="blog-trackback-link" href="javascript:HaloScanTB('rw_unique_entry_id_40=page0');"><script type="text/javascript">postCountTB('rw_unique_entry_id_40_page0'); </script></a></div></div></div> <div id="unique-entry-id-35" class="blog-entry"><h1 class="blog-entry-title"><a href="contacts_pb2.html" class="blog-permalink">New Beta of Contacts Plug-in </a></h1><div class="blog-entry-date">17/07/07 08:35 Filed in: <span class="blog-entry-category"><a href="category-other-work.html">Other Work</a></span></div><div class="blog-entry-body"><div class="essay"> <p>Here is a new beta of the Contacts plug-in. There have been fixes since the last version, primarily with Internet Explorer display issues. It is now possible to use the plug-in in production sites depending on the options selected.</p> <p>'List view' will give more reliable results than 'Cards view' but please test thoroughly before using on a public site. Most of the 'display issues' occur in Internet Explorer 6 and vary depending on the RapidWeaver theme used. They are also some CSS & Javascript problems with Safari 3 that have yet to be resolved.</p> <p>Many of the issues with the <a href="/benjamin_miller/odds_ends/files/contacts_pb1.html" title="Blog: Contacts Plug-in">last version</a> still remain. Public Beta 2 will expire on 25th September 2007.</p> <p><a href="http://www.fourbytwoflat.com/downloads/addresses_pb2.zip" title="Download the zip file">Download Contacts Public Beta 2</a>.</p> </div> <p class="blog-entry-tags">Tags: <a href="tag-rapidweaver.html" title="RapidWeaver" rel="tag">RapidWeaver</a>, <a href="tag-plug-in.html" title="Plug-In" rel="tag">Plug-In</a>, <a href="tag-web-development.html" title="web development" rel="tag">web development</a></p><div class="blog-entry-comments"><a class="blog-comment-link" href="javascript:HaloScan('rw_unique_entry_id_35_page0');"><script type="text/javascript">postCount('rw_unique_entry_id_35_page0');</script></a> | <a class="blog-trackback-link" href="javascript:HaloScanTB('rw_unique_entry_id_35=page0');"><script type="text/javascript">postCountTB('rw_unique_entry_id_35_page0'); </script></a></div></div></div> <div id="unique-entry-id-34" class="blog-entry"><h1 class="blog-entry-title"><a href="iphone_sdk_one_trip.html" class="blog-permalink">iPhone SDK</a></h1><div class="blog-entry-date">20/06/07 10:35 Filed in: <span class="blog-entry-category"><a href="category-software.html">Software</a></span></div><div class="blog-entry-body"><div class="essay"> <p>As disappointing as the lack of an iPhone Cocoa SDK was at last week's WWDC and the 'controversial' techniques developers are encouraged to use to write applications for the iPhone, there is an application that appears to substantiate Apple's suggestions.</p> <p><a href="http://onetrip.org">OneTrip</a> is a shopping list web application specifically styled and built for the iPhone. It uses UI metaphors from the iPhone's applications, is styled sympathetically and is designed with features such as drag-scrolling and screen resolution in mind. Data storage happens on the server with cookies managing session identification so there is no user registration or login.</p> <p>If all iPhone web applications are like this then maybe the current lack of a SDK it isn't such a disaster.</p> </div><p class="blog-entry-tags">Tags: <a href="tag-iphone.html" title="iPhone" rel="tag">iPhone</a>, <a href="tag-web-development.html" title="web development" rel="tag">web development</a></p><div class="blog-entry-comments"><a class="blog-comment-link" href="javascript:HaloScan('rw_unique_entry_id_34_page0');"><script type="text/javascript">postCount('rw_unique_entry_id_34_page0');</script></a> | <a class="blog-trackback-link" href="javascript:HaloScanTB('rw_unique_entry_id_34=page0');"><script type="text/javascript">postCountTB('rw_unique_entry_id_34_page0'); </script></a></div></div></div> <div id="unique-entry-id-28" class="blog-entry"><h1 class="blog-entry-title"><a href="contacts_pb1.html" class="blog-permalink">Contacts Plug-in</a></h1><div class="blog-entry-date">08/06/07 10:23 Filed in: <span class="blog-entry-category"><a href="category-other-work.html">Other Work</a></span></div><div class="blog-entry-body"><p class="summary">There's been (some) progress on the <a href="http://www.realmacsoftware.com/rapidweaver/">RapidWeaver</a> plug-in that allows users to create a page using cards from Address Book.</p> <div class="essays"> <p>I've been pretty busy working on the Church Hall conversion in my spare time so development of this plug-in has been painfully slow and I'm still far from finished but I thought I would release an early Beta for anybody that is interested in seeing progress. </p> <p><strong>Warning</strong>: <em>this plugin is not for production use</em>. It may look fine when previewed in RapidWeaver but things go downhill when the pages are exported and things get even worse when viewed in Internet Explorer.</p> <p>Some notable omissions include (but not limited to):</p> <ul> <li>No support for mobile telephone numbers</li> <li>Incorrect handling of PNGs, Javascript and CSS in Internet Explorer</li> <li>No visual feedback of 'Large Type' telephone numbers; just click them</li> <li>Incomplete Microformat support</li> <li>Dragging multiple cards, or groups, from the list or in from Address Book results in a single card being added</li> <li>Broken pagination links in List view</li> <li>Miscalculated pagination when some contacts are disabled in the list</li> <li>The Appearance tab in Setup does nothing. Still not sure how I will handle changing the appearance (If at all).</li> </ul> <p>and so it goes on… Anyway, if you haven't been put off and are still interested the <a href="http://www.fourbytwoflat.com/downloads/addresses_pb1.zip" title="Download the compressed file containing the plug-in">download</a> and have a look.</p> </div> <p class="blog-entry-tags">Tags: <a href="tag-rapidweaver.html" title="RapidWeaver" rel="tag">RapidWeaver</a>, <a href="tag-plug-in.html" title="Plug-In" rel="tag">Plug-In</a>, <a href="tag-web-development.html" title="web development" rel="tag">web development</a></p><div class="blog-entry-comments"><a class="blog-comment-link" href="javascript:HaloScan('rw_unique_entry_id_28_page0');"><script type="text/javascript">postCount('rw_unique_entry_id_28_page0');</script></a> | <a class="blog-trackback-link" href="javascript:HaloScanTB('rw_unique_entry_id_28=page0');"><script type="text/javascript">postCountTB('rw_unique_entry_id_28_page0'); </script></a></div></div></div> <div id="unique-entry-id-26" class="blog-entry"><h1 class="blog-entry-title"><a href="where_is_quicktime.html" class="blog-permalink">Where is the QuickTime I knew?</a></h1><div class="blog-entry-date">24/01/07 18:26 Filed in: <span class="blog-entry-category"><a href="category-software.html">Software</a></span></div><div class="blog-entry-body"><p class="summary">Interactive <a href="http://www.apple.com/quicktime/" title="Apple's QuickTime Home page">QuickTime</a> is pretty much dead and the QuickTime (.mov) file format is dying.</p> <p>A short while ago, whilst working on <a href="http://www.electricdecember.org/06/" title="Electric December 2006">Electric December</a>, I spent some more time working on server generated, interactive video presentation. This was an exact repeat of the previous year's Electric December development and once again I abandoned the work after a couple of weeks.</p> <p>Yes, I know this type of thing can be done easily enough in Flash but I wanted to do it in QuickTime for a few reasons:</p> <ul> <li>We want to keep the video content in MPEG4 data files because we don't want to encode very large amounts of our content in a proprietry codec.</li> <li>In my opinion, the pereformance of MPEG4 is better than the Flash On2 codec.</li> <li>We need to distribute our content in a format that can be played on mobile devices such as iPods, PSPs and of course the iPhone.</li> </ul> <p>In the Applesphere it appears that the QuickTime file format has been beaten down by MPEG4. The <a href="http://www.apple.com/appletv">Apple TV</a> doesn't appear to support QuickTime files and neither does the <a href="http://www.apple.com/ipod">iPod</a>. If the ipod doesn't support quicktime files then it is very unlikely the iPhone will. Interactivity and the other advanced features of MPEG4 are not supported in QuickTime and MPEG4 is seen as a linear audio and video format.</p> <p>Interactivity in QuickTime has been neglected and become an embarrassment for Apple. QuickTime VR is hardly mentioned these days. Have you tried opening a .m4a file with chapter artwork and links in QuickTime Player? How ugly is the result? The chapter implementation in the QuickTime UI is neat and tidy but why draw text using Times, in 'link blue' across the artwork?</p> <p>Apple recently disabled QuickTime's default ability to play Flash movies embedded inside a movie. Security issues with the existing code led Apple to turn this option off, without warning content producers this was going to happen. Flash playback in QuickTime couldn't have been a high priority. The user can re-activate the functionality, but the settings are hidden many levels down in nested preference panels, and not something you would expect an end user to perform in order to view content.</p> <p>QuickTime's XML parsing and resource loading performance makes using techniques such as SMIL or wired, umbrella movies very difficult to deploy. How long can it take to display a few, small, simple media resources such as text and still images?</p> <p>There are so many differences between the implementations of features, that haven't changed, in versions 6 and 7 of QuickTime? A content developer can't distinguish if something is working or not. Well, apart from text rendering which is not great in either. Why does QuickTime 7 install two <a href="file:///Library/Internet%20Plug-Ins/" title="Local plug-ins folder">different versions of the plug-in</a>? One, a Coca/QTKit Webkit plug-in used by Safari, and the other, a Netscape style plug-in that all the other Macintosh browsers use. It would be quite nice if these two plug-ins displayed content and behaved identically. Based on content from Apple's own site, the Quicktime plug-in is used simply to provide basic, interactive playback of linear MPEG4 video controlled by Javascript. The interface is provided by the surrounding HTML.</p> <p>It appears all the graphics & media engineers at Apple are working on technologies such as such as CoreAnimation, CoreVideo & Quartz Composer. All the advanced media technology work is done on the Mac OS X platform and not QuickTime! This is all great stuff and I love working in CoreVideo and CoreAnimation. They produce spectacular results and I amaze myself every time I click Build & Run. This is nothing we can deploy effectively to the public at large, for which the MS Windows using majority, the QuickTime plug-in is the only bridge there is. QuickTime will become a media generation framework to support Apple's pro & iLife applications, along with third party developer's desktop applications. The browser plug-in will just handle playback of MPEG4 data.</p> <p>I guess what I'm trying to say is don't waste any more time trying to use QuickTime for interactive work, use it for what it's best at: playback of MPEG4 content and leave the interactivity & interface to Javascript & HTML.</p><p class="blog-entry-tags">Tags: <a href="tag-quicktime.html" title="QuickTime" rel="tag">QuickTime</a>, <a href="tag-web-development.html" title="web development" rel="tag">web development</a></p><div class="blog-entry-comments"><a class="blog-comment-link" href="javascript:HaloScan('rw_unique_entry_id_26_page0');"><script type="text/javascript">postCount('rw_unique_entry_id_26_page0');</script></a> | <a class="blog-trackback-link" href="javascript:HaloScanTB('rw_unique_entry_id_26=page0');"><script type="text/javascript">postCountTB('rw_unique_entry_id_26_page0'); </script></a></div></div></div> </div> </div> <!-- End Content --> <div id="controls"> <h2><span class="t-down">odds</span>&<span class="t-up">ends…</span><br /><span class="subTitle">Benjamin Miller</span></h2> <div id="mainNavigation"><ul><li><a href="../" rel="self" id="current">Blog</a></li><li><a href="../links/" rel="self">Links</a></li><li><a href="../me/" rel="self">Me</a></li></ul></div> <div id="blog-categories"><div class="blog-category-link-disabled">Personal</div><a href="category-watershed.html" class="blog-category-link-enabled">Watershed</a><br /><a href="category-other-work.html" class="blog-category-link-enabled">Other Work</a><br /><a href="category-dshed.html" class="blog-category-link-enabled">dShed</a><br /><div class="blog-category-link-disabled">Church Hall</div><a href="category-this-site.html" class="blog-category-link-enabled">This Site</a><br /><a href="category-software.html" class="blog-category-link-enabled">Software</a><br /><div class="blog-category-link-disabled">Web de</div></div><div id="blog-archives"><a class="blog-archive-link-enabled" href="archive-feb-2009.html">Feb 2009</a><br /><a class="blog-archive-link-enabled" href="archive-jan-2009.html">Jan 2009</a><br /><a class="blog-archive-link-enabled" href="archive-aug-2008.html">Aug 2008</a><br /><a class="blog-archive-link-enabled" href="archive-jul-2008.html">Jul 2008</a><br /><a class="blog-archive-link-enabled" href="archive-jun-2008.html">Jun 2008</a><br /><a class="blog-archive-link-enabled" href="archive-jan-2008.html">Jan 2008</a><br /><a class="blog-archive-link-enabled" href="archive-jul-2007.html">Jul 2007</a><br /><a class="blog-archive-link-enabled" href="archive-jun-2007.html">Jun 2007</a><br /><a class="blog-archive-link-enabled" href="archive-jan-2007.html">Jan 2007</a><br /><a class="blog-archive-link-enabled" href="archive-jul-2006.html">Jul 2006</a><br /><a class="blog-archive-link-enabled" href="archive-jun-2006.html">Jun 2006</a><br /><a class="blog-archive-link-enabled" href="archive-may-2006.html">May 2006</a><br /><a class="blog-archive-link-enabled" href="archive-apr-2006.html">Apr 2006</a><br /><a class="blog-archive-link-enabled" href="archive-feb-2006.html">Feb 2006</a><br /><a class="blog-archive-link-enabled" href="archive-jan-2006.html">Jan 2006</a><br /><a class="blog-archive-link-enabled" href="archive-dec-2005.html">Dec 2005</a><br /><a class="blog-archive-link-enabled" href="archive-nov-2005.html">Nov 2005</a><br /></div><ul class="blog-tag-cloud"><li><a href="tag-3gp.html" title="3GP" class="blog-tag-size-6" rel="tag">3GP</a></li> <li><a href="tag-applescript.html" title="AppleScript" class="blog-tag-size-7" rel="tag">AppleScript</a></li> <li><a href="tag-art.html" title="Art" class="blog-tag-size-5" rel="tag">Art</a></li> <li><a href="tag-bluetooth.html" title="Bluetooth" class="blog-tag-size-5" rel="tag">Bluetooth</a></li> <li><a href="tag-cocoa.html" title="Cocoa" class="blog-tag-size-10" rel="tag">Cocoa</a></li> <li><a href="tag-core-audio.html" title="Core Audio" class="blog-tag-size-4" rel="tag">Core Audio</a></li> <li><a href="tag-core-data.html" title="Core Data" class="blog-tag-size-4" rel="tag">Core Data</a></li> <li><a href="tag-core-image.html" title="Core Image" class="blog-tag-size-6" rel="tag">Core Image</a></li> <li><a href="tag-eof.html" title="EOF" class="blog-tag-size-4" rel="tag">EOF</a></li> <li><a href="tag-iphone.html" title="iPhone" class="blog-tag-size-3" rel="tag">iPhone</a></li> <li><a href="tag-java.html" title="Java" class="blog-tag-size-8" rel="tag">Java</a></li> <li><a href="tag-plug-in.html" title="Plug-In" class="blog-tag-size-8" rel="tag">Plug-In</a></li> <li><a href="tag-print.html" title="Print" class="blog-tag-size-3" rel="tag">Print</a></li> <li><a href="tag-quicktime.html" title="QuickTime" class="blog-tag-size-9" rel="tag">QuickTime</a></li> <li><a href="tag-rapidweaver.html" title="RapidWeaver" class="blog-tag-size-10" rel="tag">RapidWeaver</a></li> <li><a href="tag-rest.html" title="REST" class="blog-tag-size-7" rel="tag">REST</a></li> <li><a href="tag-safari.html" title="Safari" class="blog-tag-size-2" rel="tag">Safari</a></li> <li><a href="tag-unfuddle.html" title="unfuddle" class="blog-tag-size-2" rel="tag">unfuddle</a></li> <li><a href="tag-web-development.html" title="web development" class="blog-tag-size-10" rel="tag">web development</a></li> <li><a href="tag-web-kit.html" title="Web Kit" class="blog-tag-size-1" rel="tag">Web Kit</a></li> <li><a href="tag-webobjects.html" title="WebObjects" class="blog-tag-size-9" rel="tag">WebObjects</a></li> <li><a href="tag-wwdc.html" title="WWDC" class="blog-tag-size-1" rel="tag">WWDC</a></li> <li><a href="tag-xml.html" title="XML" class="blog-tag-size-7" rel="tag">XML</a></li> </ul> <div id="blog-rss-feeds"><a class="blog-rss-link" href="feed.xml" rel="alternate" type="application/rss+xml" title="Odds & Ends">RSS Feed</a><br /><a class="blog-comments-rss-link" href="http://www.haloscan.com/members/rss.php?user=benjaminmiller">Comments Feed</a></div> <!--<script type="text/javascript" src="http://www.makepovertyhistory.org/whiteband_small_right.js"> </script><noscript><a href="http://www.makepovertyhistory.org/"> http://www.makepovertyhistory.org</a></noscript> <p>Posts that contain <a href="http://technorati.com/search/%22watershed+media+centre%22">"watershed Media Centre"</a> per day for the last 30 days.<br /><a href="http://technorati.com/search/%22watershed+media+centre%22"><img src="http://technorati.com/chartimg/%28%22watershed%20media%20centre%22%29?totalHits=23&size=s&days=30" style="border:0" alt="Technorati Chart" /> </a></p>--> </div> <!-- End Controls --> <div id="footer"><p><a href="/benjamin_miller/odds_ends/files/feed.xml" title="Subscribe to the Rss News Feed">rss feed</a> & <a href="/benjamin_miller/odds_ends/colophon/index.html" title="How and what is used to make this site">colophon…</p></div> </div> <script type="text/javascript" src="http://cetrk.com/pages/scripts/0008/8709.js"> </script> <!-- Start Google Analytics --> <script type="text/javascript"> var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www."); document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E")); </script> <script type="text/javascript"> var pageTracker = _gat._getTracker("UA-200933-2"); pageTracker._initData(); pageTracker._trackPageview(); </script><!-- End Google Analytics --></body> </html>