Professional Documents
Culture Documents
P Shearan
October 2007
Faculty of Business, Computing & Information Management
ABSTRACT
ACKNOWLEDGEMENTS
Bellsy, without your constant support, encouragement and editing this never would have
happened.
Thommo, thanks for the kick up the “you know where” when I needed it most.
Mark Josephs, my tutor, thank you for all the invaluable support, help and input.
Table of Contents
1 INTRODUCTION .............................................................................................................................. 7
2.1 WEB 1.0 TO 2.0 AND EVEN 3.0. THE CHANGING LANDSCAPE .......................................10
2.1.1 Web 1.0 ..................................................................................................................................10
2.1.2 Web 2.0 ..................................................................................................................................11
2.1.3 Web 3.0 ..................................................................................................................................13
2.2 WHERE ARE WE NOW? ..........................................................................................................14
2.2.1 What currently runs the Server-Side of the Web? ..................................................................14
2.2.2 Web Application and Systems Development Tools ................................................................15
2.2.3 Comparing Typical Web 1.0 and Web 2.0 Application Architecture .....................................17
2.3 WEB 2.0 APPLICATIONS IN DETAIL ....................................................................................19
2.3.1 A Business Layman’s view of Web 2.0 ...................................................................................19
2.3.2 Web 2.0 – the O’Reilly Way ...................................................................................................19
2.3.3 Mapping Technology to Web 2.0 Services .............................................................................21
2.3.4 Breaking the Page .................................................................................................................21
2.3.5 Tim O’Reilly’s Hierarchy of “Web 2.0-ness”........................................................................22
2.3.6 Ajax ........................................................................................................................................22
2.3.7 Blogs ......................................................................................................................................22
2.3.8 Mashups .................................................................................................................................23
2.3.9 Tagging ..................................................................................................................................24
2.3.10 Web Services .....................................................................................................................24
2.3.11 Web Syndicated Feeds (RSS and Atom) ............................................................................25
2.3.12 Wikis .................................................................................................................................27
2.4 THE WEB DEVELOPERS CONUNDRUM .............................................................................27
REFERENCES ............................................................................................................................................74
BIBLOGRAPHY.......................................................................................................................................... 77
Table of Figures
FIGURE 1: STAGES AND MAJOR LANDMARKS OF THE WEB ..........................................................10
FIGURE 2: PHP UTILISATION ON THE INTERNET, MARCH 2007 .....................................................14
FIGURE 3: VERSION DISTRIBUTION ON INTERNET WEBSITES RUNNING PHP, MARCH 2007 .14
FIGURE 4: TOP TEN MOST POPULAR PROGRAMMING LANGUAGES, AS SURVEYED BY
TIOBE SOFTWARE, AND THEIR RELATIONSHIP TO WEB DEVELOPMENT. .......................15
FIGURE 5: ANATOMY OF A WEB 1.0 APPLICATION ..........................................................................17
FIGURE 6: ANATOMY OF A WEB 2.0 APPLICATION ..........................................................................18
FIGURE 7: THE O'REILLY WEB 2.0 MEME MAP ...................................................................................20
FIGURE 8: VISUALISING WEB 2.0 (DON HINCHCLIFFE – SOCIAL COMPUTING).........................21
FIGURE 9: A WEB SYNDICATED FEED THROUGH GOOGLE READER ...........................................26
FIGURE 10: NODES IN A DOM TREE ......................................................................................................30
FIGURE 11: ANATOMY OF AN AJAX REQUESTANATOMY OF AN AJAX REQUEST ...................33
FIGURE 12: DEVELOPMENT ENVIRONMENT SELECTION CRITERIA ............................................40
FIGURE 13: UNIT TEST EXAMPLE ..........................................................................................................46
FIGURE 14: FUNCTIONAL TEST EXAMPLE ..........................................................................................47
FIGURE 15: COMPARISON OF AMOUNT OF CODE IN PHP AND ROR APPLICATION ..................59
Page 7 of 126
1 INTRODUCTION
In late 2004, a conference was organised by O‟Reilly Media and MediaLive International
[1]
. The topic of the conference was the newly coined phrase Web 2.0. This represented
While many well known websites have emerged using Web 2.0 technology and
techniques (GoogleMaps, Flickr, Facebook, del.icio.us) a large number of sites are still at
the Web 1.0 stage. A key part of this dissertation is to identify some important Web 2.0
features and demonstrate how these can be implemented using one of the new framework
environments, designed for implementing these features, and how much effort is required
The advancement of the capabilities of web applications and the realisation of new web
techniques over the last three years has led to change in the expectations and
these clients, the needs are simply to provide an up to the minute web experience
to their customers that will involve all the buzz words and capabilities of the
moment – Web 2.0, the dynamic Web desktop, Web Services, Tagging, Mashups,
To the web application developer the challenge is to stay ahead of the market in
customers. Along with this the developer needs to provide quality software, which
Page 8 of 126
is easy to test, maintain, enhance and reuse. Does this happen now with Web 1.0
A recent survey shows [13] that over 50% of all websites are still produced using well
established but difficult to maintain environments using procedural type languages and
development environments.
The proliferation of new web frameworks (discussed in chapter 3) indicates that through
design paradigm, web application developers will have the ability to meet the new
requirements of their customers to provide Web 2.0 type applications. The largest barrier
to this transformation for web application developers is the “ramp up” time, or time
required to identify, install, learn and deploy a new development environment and
produce competent, efficient and re-usable code in this environment while still managing
independent research this report seeks to identify the possible transition path from
tradition procedural coding such as PERL, PHP and ASP to the more modern object
oriented approach of Ruby on Rails (RoR), Django Python and CakePHP MVC
environments. These MVCs were chosen as they specifically address the needs of the
web designer and will particularly focus on the development of an application using RoR.
Identify and discuss, through research of academic journals, articles and papers
transformations or “sea changes” of the World Wide Web. Through this research
the need for a change of development paradigm is identified for the web
application developer, this needs to be balanced against the need for fast and
web desktop, and provide further insight into the business case to provide this
facility.
Discuss a number of Object Oriented (OO) MVC frameworks that have emerged
to allow development of Web 2.0, specifically RoR, Django Python and Cake
PHP. As the selected framework RoR will be discussed in more detail and the
Introduce and explain the practical application, Family Scrapbook, that will be
application will be used to provide an insight into the learning curve that will need
2.1 WEB 1.0 TO 2.0 AND EVEN 3.0. THE CHANGING LANDSCAPE
As with all things in the computing world a definitive, concise and universally agreed set
of defined terms for the evolution of the World Wide Web proves difficult to identify but
Tim Berners-Lee, 1996 [2] original vision of the World Wide Web was:
people could communicate with each other and with machines. Since its
inception in 1989 it has grown initially as a medium for the broadcast of read-
only material from heavily loaded corporate servers to the mass of Internet
connected consumers.”
Page 11 of 126
Web 1.0 was the original explosion of websites filled with static pages, accessed
through web browsers and using urls to send http requests. Web 1.0 went through
many iterations with the adoption of technologies such as CGI, xHTML, XML, CSS,
and JavaScript.
Within Web 1.0 the majority of the processing of information is performed on the
server-side using procedural scripting languages (PHP 4, Perl, Tcl, ASP). The server-
Tim O‟Reilly, 2006 [3], whose company is credited with coining the term Web 2.0 and
is considered to be the most authoritative voice on the subject (Dion Hinchcliffe, 2007
[4]
) says:
“Web 2.0 is the business revolution in the computer industry caused by the move
to the Internet as platform, and an attempt to understand the rules for success on
that new platform. Chief among those rules is this: Build applications that
harness network effects to get better the more people use them.”
along with a change in emphasis in the deployment of websites. The primary driver of
Web 2.0 is to provide seamless applications, which gain acceptance and improvements
from user participation, without the requirement to upgrade or even know the version
of the application you are using. O‟Reilly also details a four level model of
Page 12 of 126
compliance to the ethos of Web 2.0 for Web Applications, this is detailed in chapter
2.3.4.
2005 [5] is one of the main components of Web 2.0, it allows the changing of the
contents of a web page without the necessity to reload the entire page or use artificial
methods of providing dynamic updating of content e.g. frames [6]. The use of the Ajax
from the server-side to the client-side. While much of the communication between the
client-side and the server-side is expected to be XML using XHR, it will allow for
other methods like JavaScript Object Notation (JSON) and even text. Ajax should lead
Web 2.0 also introduces the idea of community information, sharing information not
just as pages served to the browser but made available by a number of methods such
as tagging and RSS through to Web Services. Further, Web Services introduces the
Fielding (2000)[7]. While details of community information and web services are
beyond the scope of this research, it again indicates the importance of XML in the
Ajax, Blogs, Mashups, Social Computing, Tagging, Wikis, Web Services, Web
Syndicated Feeds (RSS and ATOM) are a short glossary of terms of the key concepts
and technologies associated with Web 2.0. This is not an exhaustive list but a good
indicator of the term Web 2.0. These terms will be explored in more detail in
following sections
Page 13 of 126
The basic idea behind the Semantic Web, also referred to as Web 3.0[8], is to make all
information on the Internet into a huge indexed database using many techniques. The
Resource Description Framework (RDF) and Web Ontology Language (OWL) are
seen by Schreiter (2007)[9] as core technology in the adoption of the Semantic Web.
In enabling Web 3.0, Web Services - using standards such as REST, SOAP and
WSDL - are key and will be provided using XML as the unifying language says
addressed as part of the Semantic Web. In his talk to the W3C, Nykanen (2002) [11]
describes that:
"The Semantic Web brings to the Web the idea of having data defined and
linked in a way that it can be used for more effective discovery, automation,
Work on the Semantic Web continues, at pace, within the research and academic
community and generates much press but no real commercial mainstream deliverables
This proposal does not seek to be comprehensive on the past and future of the web but is
merely a scene setter, for example no discussion has been included on the relative merits
In March 2007, Nexen.net [13] identified in a survey over 10 million websites (Figure
2) that nearly 34% of websites used PHP as the server side environment, nearly 22%
used Microsoft ASP and 44% had no discernable server-side environment (likely to
Of the PHP environments identified only 16% were PHP 5 and above, see Figure 3.
While these figures cannot be taken as definitive they do indicate that PHP is the
dominant server-side development tool in the open source web server side
environment. These figures indicate that over 50% of websites still use procedural
technologies.
In her recent article Paulson (2007) [14] identified the ten most popular programming
languages provided in this article has been expanded to add information on how or if
language, and if they are relevant to Web 1.0 or Web 2.0 and produced as Figure 4.
development environments. Java is used extensively in the web but often in major
systems development. C and C++ are used almost exclusively in major system
PHP and Perl are currently deployed in the majority of Web 1.0 server-side
applications in the LAMP environment and will use JavaScript on the client-side. In
Interestingly, three Web 2.0 style languages are in the top ten – C#, Python and Ruby,
for some time but are now being designed specifically for web application
development.
While web developers are aware of alternatives to the current LAMP type web
development environment, there is concern about the ramp up time for learning and
paradigm.
Page 17 of 126
2.2.3 Comparing Typical Web 1.0 and Web 2.0 Application Architecture
The anatomy of a typical Web 1.0 website, Figure 5, is generally described as client-side
(requests for and rendering of (x)HTML documents and request for plug in applications)
and the server-side (web server for handling requests from clients and passing on requests
to the scripting and database environment through the Common Gateway Interface
(CGI)). CGI (or similar if ASP) applications are mainly built using PHP 4, MS
Figure 6 suggests an initial model and introduces the concept of the MVC framework
Web 2.0 application. A number of these techniques will be implemented in the practical
insight into the viewpoint of non-computing professionals on what Web 2.0 means to
them. In a specialist accounting publication, The CPA Journal, an article of Web 2.0
“Web 2.0 fills the gap between a web browser and desktop applications. It brings
together documents and data scattered over local computers and the Internet, and
In an interesting perspective on Web 2.0 for uses of things other than social
computing, the article points out that small firms (like the accountancy firms this
journal addresses) will gain the benefit of web based business application services that
will become available to them “because they will cost only a fraction of what
produced as part of the kick off meeting for the joint O‟Reilly and CMP conference in
While I have struggled to identify an exact definition of what the different areas on the
The bottom layer of flesh coloured bubbles represent a set of vague or loosely
The middle box contains the firm concepts or mission statement for the
The Web 2.0 meme map represents a set of ideals and capabilities required from new
web applications but does not seek to address the enabling technologies.
Page 21 of 126
“(Web 2.0) is about breaking that core principle of the web (the page) and
partial pages, and code between the browser and the server to provide a more
communications without the need for screen scraping and post processing of pages.
Through the use of web services and web syndication feeds – along with the
gathered from third party sites without the need for them to be informed.
Page 22 of 126
Leve1 3 An application that can ONLY exist on the web. The communication between
people and application is made possible by the connectivity of the Internet. The
more people use the application, the better the application becomes because of
the added richness of content. Good examples of this are WikiPedia and
Amazon reviews.
Level 2 An application that can exist without the web but becomes much richer when
using it. The application is a useful tool to an individual but as they share the
information with others the better it becomes. Good examples are Flickr, where
an individuals set of photos gain added strength from sharing and tagging and
BaseCamp, where individuals can manage a project but gain further
productivity by sharing project information with others.
Level 1 An application exists offline but gains additional features when being online.
An example of this would be iTunes which is a music and video mangement
system but becomes a media jukebox and store when connected to the Internet.
Level 0 The application was launched as an web application but if the information was
locally cached it would still work as well. MapQuest, Yahoo! Local and
GoogleMaps are bracketed in this layer by O‟Reilly but I would argue that
GoogleMaps with it‟s extensive API and publishable maps falls into Level 2.
2.3.6 Ajax
Ajax will be covered in great detail in Chapter 2.5.
2.3.7 Blogs
Blogging is one of the earliest and most widely used forms of social computing and
The Web Log or Blog, as it is more commonly known, is a replacement for the
personal web page where an individual created web content using html or an html
editor. Blogging sites, there are too many to mention, have replaced the need for hard
work and maintenance of a website with an easy to use web application. Many of the
Page 23 of 126
social networking sites like FaceBook, MySpace and Bebo also provide blogging
facilities.
A Blog is presented in the form of a reverse chronological list of entries and allows
readers to add comments and links to the blog. The blogging application will normally
provide an editor and allow the creation of a template to control the styles, fonts and
colours of the entries or will allow pasting from the authors favourite editor.
Most Blog applications incorporate a web syndication facility, which allows interested
parties to keep abreast of the latest entries, in the Blog without the need for regularly
2.3.8 Mashups
A mashup is a web application that combines it‟s own information with the
information and content source from third party websites. The third party will not be
aware of the use of it‟s content and the mashup will normally add value to the source
In this dissertation the practical application will use the api from GoogleMaps to
overlay pin markers to a location on a map and link to photos, reviews and blogs
associated with that location. This is a mashup as it uses GoogleMaps API in a way in
which they have no control. Other sources like web services and web syndication
feeds may then be added in to the application to further extend the mashup.
Page 24 of 126
2.3.9 Tagging
Tagging is a way of associating and identifying commonality between various
disparate entities. The tagged entity could be such items a web page, a photo, a blog
by some of the most respected sites on the Internet. For example, Amazon, Yahoo
and Google allow their users to categorize and link together a variety of
Tagging will be used in the practical application for this dissertation. Photos will be
able to be tagged by the owner and individuals. Searches will then be available on a
information from a remote source. A remote web service will provide a definition of
the objects available for query and the methods that can be used to access the object.
Web services can be open and freely accessible or limited by secure connections.
A number of architectures exist to provide web services but the three most popular are,
SOAP is a World Wide Web consortium (w3) standard and an extension of the more
SOAP further extends the content delivery through enriching the XML document to
define the service using the Web Service Description Language (WSDL). Most web
frameworks, including RoR and CakePHP provide support for web services that will
REST adopts CRUD (Create, Read, Update, Delete) and maps them to the standard
POST, GET, PUT, and DELETE operations available in HTML which are often
mapped onto the four basic SQL operations INSERT, SELECT, UPDATE, and
and uses universal resource identifiers (URIs) rather than method calls.
As always, a debate rages in the computing world over the relative merits of SOAP
and REST, though in an interesting development RoR release 2.0 will enhance its
native support for REST while dropping it for SOAP and XML-RPC (they will still be
available as a plug-in).
In the practical piece of this dissertation will use the GoogleMap API web service to
gather geocoding information for the location object and plot it as a pin marker.
another main advance of Web 2.0 technology. The idea of a syndicated feed is to
Page 26 of 126
“push” out the latest updated information from a website therefore removing the need
BBC news website through to an individuals Blog, using either Really Simple
technology.
as Google Reader) with the syndication feed, normally by dragging and dropping the
icons in the above paragraph. The browser or news reader then regularly checks for
changes and updates. The browser or reader will then display the headlines, see figure
9, provided in the XML document provided by the syndicated feed along with a link to
2.3.12 Wikis
A wiki is a lightweight web database that allows contribution and editing of entries
through a standard browser. While some entries are controlled and invigilated, many
The most well know Wiki (and one of the best examples of Web 2.0 in context) is
Wikipedia.
search tools to an idea of community information identified by tags, and served out by
RSS channels and the need to introduce Web Services. There is also the need to
incorporate blogging and wikis into web applications. Also, XML is the leading
facilitator of information sharing in Web 2.0 and beyond and is a cornerstone of the new
technologies.
These changes in use of the Web translate into the need for development or upgrade of
websites using an appropriate paradigm. Without these changes vast tracts of existing
websites may become obsolete due to their inability to incorporate these functions.
To ensure website development meets the requirements of Web 2.0 and Web 3.0, web
development thus suffering the shorter term pain of learning new techniques and
tools
Page 28 of 126
OR
b. Wait to see how the new techniques are adopted and potentially be left behind by
Developers of web applications need to consider a balance between the need to deliver
new content to tight customer deadlines and looking to future proof their work with some
design considerations.
of a website, „Family Scrapbook‟, using the RoR MVC framework. The main focus of
the „Family Scrapbook‟ will be the creation and display of associated photo and location
information.
While it is intended to incorporate a number of the Web 2.0 techniques discussed in the
previous chapter, it is expected that this application will make widespread use of AJAX
to give the web pages a more dynamic, active desktop like feel.
Pages will be updated without the need for page reloads and highlighting effects will be
technology but a neat term to describe the ability to create dynamic web client side
content. Garrett‟s acronym while not radical in technology terms, no new language or
development paradigm was proposed, neatly encapsulated the basic techniques and
applications. The term AJAX became a label and easy to comprehend concept for
dynamic web client taking the technologies out of the development labs and into the
marketplace.
A - While AJAX can and often does act Asynchronously, allowing the user
continue working on a page while awaiting the return updates from an AJAX
request. However, AJAX can also be set to block further user activity until
J - JavaScript is not the only client side language available – Visual Basic and
Flash scripting can also be used to call the core XHR object. In fact, XMLHttp
was originally implemented as an ActiveX object and was solely used by Visual
X - XML is not the sole communications mechanism used by the XHR object.
HTML, JavaScript Object Notation (JSON), plain text, images etc can all be
“Ajax is the use of browser native technology (e.g. JavaScript and the DOM, but
not Flash) to decouple the user interaction processes from server communication
processes”
One thing the AJAX acronym does not credit (though Garrett does in his article) is the
importance of standardised XHTML and CSS adherence in the web client, namely the
Page 30 of 126
structure to a web page that allows traversal through a XHTML document as an object
[22]
Figure 10: Nodes in a DOM Tree
In Figure 10, the DOM contains an element node (Invoice) which contains attribute
nodes and sub elements. This organisation is key to the effective use of AJAX. Using
update the value of the Invoice.City attribute which would be updated on the web
page.
While the name AJAX is often derided as a marketing term due to its inaccuracy, the
important issue is that the Garrett‟s article was the point that married common
product managers and entrepreneurs to produce the elevator pitch [23] to raise funding
addressed. These include the back button, saving links and providing rich media
content.
in the first piece of work when uploading photos using AJAX and PHP), most books
recommend the use of the freely available Prototype and Script.aculo.us JavaScript
framework libraries. These libraries are bundled with RoR and most of the other web
MVC frameworks
Prototype simplifies the process of creating XHR objects, provides error handling and
browser type discovery through its AJAX object. The two main methods are
AJAX.Request, which provides an object with the response from the remote server, or
AJAX.Updater, which will actually update the webpage attribute without the need for
Page 32 of 126
further coding. I have used AJAX.Request in the GoogleMap mashup of the Family
Scrapbook application.
Script.aculo.us is built on the Prototype and provides the many screen effects used
throughout the Family Scrapbook application. Any effects seen on the web page after an
Ajax update e.g. highlighted images, blind down effects and the like are all part of the
Script.aculo.us framework. Script.aculo.us also provides the drag and drop capabilities
An event on the web page (mouse click, clicking a submit button etc) will
The JavaScript event will create an AJAX request via the XHR object and send
The server will handle the request and return results in XML, JSON or HTML
JavaScript will then use the results in the XHR object to update the appropriate
writing general purpose components that can be re-used throughout the main
All the development environments under consideration support and attempt to enforce
the MVC architecture on web applications under development. The main idea of the
usually held in a database) from the output mechanism (usually pages in a browser).
“When developers first started producing web applications, they went back to
An MVC uses the controller to handle requests coming from a requesting source,
browser or web service, interacts with the model to gain the required knowledge and
Page 35 of 126
passes the knowledge to a view which is rendered in the appropriate format to the
requesting source.
In web application models, a single model generally represents the object definition
of a database table. It enforces validations, which could include ensuring that certain
fields must be present for an object to be permanently stored by the model, enforcing
enumerators so that a file type field must conform to a limited number of options.
Also, business logic can be enforced by the model e.g. the minimum price of a
All calls from the controller to get information are handled by the model, so the
actual underlying database is unknown and irrelevant to the controller. This means
that a model could move from MySQL to Oracle without any changes being made to
the rest of the application, as it would be transparent to anything but the model.
The view provides the output from the application and presents data provided by the
model through the controller. The view has no knowledge of the contents of the
If a „scrapbook‟ view is passed 100 „photo‟ objects to display, it will know what size
image to show for each photo and how many photos to display per line but the actual
names and locations of the photos will be passed to the view in an object from the
A view may also present a form to the end user asking for information to input but
any processing of the information put into that form will be handled by the controller
The controller handles and validates incoming user requests from the users of the
application. If appropriate it will get from or store information in the model and ask
There are some basic differences between the two development paradigms.
unexpected ways.
easy to read.
Object-Oriented– creates a model of a real world object called a class, that defines
properties of the object and the methods on the way that object
can inherit the properties and methods of another object e.g. a man
Page 37 of 126
object would inherit a human object and a father object would inherit
strictly controlled by the methods of the class that provides access to the state of the
performance without affecting the whole system as long as the methods still perform
the tasks stated originally. In the MVC architecture discussed earlier in this chapter,
The other major advantage is the ability to build testing throughout the lifecycle of the
application through class tests. In the terms of the three development environments
discussed in this thesis, Unit testing performs class tests. Each class can be treated
individually to ensure that the object handles all input and output without the need for
a fully working application. This means that each object can be created, maintained
and implemented as individual building blocks for the application in the knowledge
used in Sprint 0 of the implementation. Early in the development cycle the photo files
were stored by the object in directories representing the gallery and were retrieved
from these directories when requested. Later the photos were stored in a single
Page 38 of 126
directory and a database entry was made for each photo denoting which gallery was
the photos home gallery. These changes were made in the object but retained the same
interface/method behaviour. The page designer did not need to know any changes
3.1.6.1 Django
Django is the Python Web Framework. Python is an object oriented scripting
language, which has gained a large user base. Django is a relatively new framework
Django utilises a MVC framework and includes support for AJAX and syndication
feeds.
oriented development techniques. The market share of RoR is growing fast and claims
designed specifically for web development including all the functions required for
Web 2.0.
3.1.6.3 CakePHP
CakePHP is rapid development framework for PHP and is inspired by RoR (this is a
quote from the CakePHP website [26]). It provides an MVC framework for developing
web applications in PHP. CakePHP supports both PHP 4 and 5, the MVCs can be
A major decision in this thesis was the selection of an appropriate tool to produce the
In the initial proposal document for the dissertation I suggested that the practical
environments – PHP 5 and RoR. Some time was spent on designing the initial piece
of the practical application, the photo uploading facility, in PHP 5 using its object
capabilities.
Unfortunately, when I moved to produce the second part of the application, the photo
with RoR was completely misguided, the framework environment was far superior
and quicker to produce product and test through. As has been detailed at length in
framework. It is unfair to try and compare PHP 5 to RoR but that the comparison
Having narrowed the field to three possible development environments and having
had an abortive and time-consuming diversion into developing an object model with
The criteria used, detailed in Figure 12 were based on the ability to support an open
have books around to refer to rather than on line references – it is a personal choice).
Apart from time to install, RoR was equal or superior to the other two platforms.
CakePHP was quick to install, RoR provided awkward to locate and install a native
driver for MySQL 5 and Django required numerous „makes‟ to install correctly.
Page 41 of 126
This section looks to provide an overview of how RoR was developed, it‟s main
advantages and introduces the RoR concept of embedded test driven development.
“Ruby on Rails is a MVC framework that makes it easier to develop, deploy and
programming language written by Yukihiro Matsumoto and first released in 1995 [28].
While it gained popularity in Japan, it was little used in Europe or the United States
language that supports dynamic typing, unlike Java and C++ which are said to be
statically typed.
Dynamic typing allows more flexibility in the way the variables are assigned a type
when the program is running, an integer can become a string or more importantly a
decimal without the need for specific transformation code and will rely on validation
in the object before being stored in, for example, a database. Statically typing enforces
strict adherence to the defined variable type and unless specifically allowed within the
program will produce an error if assigned a new data type without being handled by
Page 42 of 126
the object. Dynamic typing allows for more flexibility but can cause issues at
validation or storage time or with calculations if the variable has been transformed to
behaviour due to variable types during program execution time. The distinction
between dynamic and static typing is an area of heated debate in the development
community – just google “static vs dynamic types” to get a feeling for this but the
added to a running application without the need for recompilation and provides a
RoR provides all the tools necessary to create a web application except a persistent
database and a web server. It is provided through an open source license and can be
installed on all major platforms including many Linux variants, Apple Mac OS X and
MS Windows. DHH and 37 Signals are keen Mac OS X exponents and recommend
While RoR does not provide an underlying database, support is provided for all the
major open source databases including MySQL, Postgres and SQLite and a number of
The RoR framework provides a small lightweight web server called WEBrick that can
production web server as it is single-threaded and lacks the security, and maturity of
Guiding principals of RoR are the following (and reflect the introduction to Ajax on Rails
by Scott Raymond [29]):
Extracting the infrastucture of the original project, the Basecamp application, and
the user led requirements for an MVC framework that would easily and
consistently create web applications. Each new iteration of the RoR development
community, so that any new feature or library within the framework reflects a real
rails new_application_name
A complete directory structure containing all the required elements to run a new
application along with the necessary configuration and template files are created.
The app directory contains three sub directories controllers, models and views –
this is where RoR expects the files relating to these parts of the framework to
exist. The db directory contains the database schema, the configs directory
All these conventions could be overridden if wished but mean that the developer
may be argued that this is a restrictive environment but it really does concentrate
developers on development.
Opinionated Software
Terrible name, great concept! If you want a database table to contain photos
[plural] then you ask RoR to create a migration called photo [singular]. RoR will
create a database table called photos with a unique column called id for each row
and a model in the framework called photo. It will now understand that photos are
photo_id, and a single line is added to the photo and album model files, RoR will
automatically assume that this relates to a unique id field in the photos table.
Without any major configuration work, RoR has assumed that there is a one
As with all things, this “opinion” can be overridden in the configuration of the
model.
There should be only one, authoritative source for each piece of information. This
removing storage of data from the output source. Any view that is requested from
a web browser or a web service is rendered by the controller and updated with the
current state of the model. The rendering negates the need for pre-formatted pages
Page 45 of 126
and ensures that duplicate and potentially out of date information is not output to
If a user requests to view the photos in an album, the controller will ask the model
for the current contents of the album and pass that to the view for formatting. All
the view needs to do is format the output and needs no knowledge of the previous
web applications and heavily reliant on manual tests. Without testing and the ability to
perform regression testing (testing that everything that used to work before a new
feature was added still works) has been time consuming and poorly done.
TDD assumes the following four steps in each stage of development [30]:
Ensure the test fails (as the code hasn‟t been written yet)
RoR provides three types of testing, unit, functional and integration that are built
round the Test class using assert methods. An assertion will either be true and a test
shows a simple unit test that creates a new Photo object then tests that the object
responds to the correct name and method and that the value of the photoName
Functional tests are used to test complete controllers and imitate the calls made by
web browsers and web services. They are tests for use cases to ensure that a call
name and „shearan‟ as the password. The test assertions then check that a user
session has been created and that the user is then redirected to the next page. If these
are true then the tests will pass. The second test posts incorrect user values and uses
test assertions to ensure that the user session was not created and that the user was
not redirected to the next page (the assert_response :success represents the object
handling the incorrect login correctly). These tests ensure that the login part of the
Integration tests can span controllers and would allow a complete end to end test. For
example an integration test would combine the tests shown in Figures 13 and 14to
prove that a user called „peter‟ could successfully log onto the system and add a new
provides the project design plan and includes discussion pieces on discoveries and
As this MSc is a conversion course, it is not intended that the practical application will be
a production ready, tested application. The application is support for the research project
Any major conclusions will be provided in the final section of the document.
Database design and storyboards will be included in the appendices to this document.
Firstly, is the application and implementation of skills learnt on the previous units of
this MSc course in Internet and Multimedia Systems.
It comes with extreme difficulties if the new development environment uses a new
paradigm (e.g moving to object orientation), a new platform (e.g linux to windows) or
if a current system needs to be maintained
The practical element of this project will be the definition and development of a web
based application to share multimedia alongside relevant blogging and location
information between family and friends.
The applied research will investigate various parts of the anatomy of a web
application; identify the main components and processes within that part; and
demonstrate the differences between a transactional and an object-oriented approach
to the development and deployment of that part
While this functionality is not unique and there are some commercial applications (e.g.
Flickr) which provide many facilities of the practical application that is irrelevant. The
practical application is an example of the use of Web 2.0 technologies implemented
using an object-oriented paradigm, demonstrating the advantages of this approach over
the transactional approach.
As there are limited training courses, I will attempt to learn RoR using the tutorials
and books recommended on the RoR website, http://www.rubyonrails.org/.
I intend to start the learning process in mid-June 2007 and be ready to start work on
the practical application by 1 September 2007.
The development and testing work will be done on an Apple MacBook running Mac
OS X 10.4 (Tiger). This was chosen as the development work can be undertaken at
any physical location and can act as a self contained environment.
The production system will be a x86 Intel platform running Red Hat Fedora 6, a linux
based open source operating system. This system already hosts my personal website
www.shearan.com and is accessible on the Internet.
I decided at this point I needed to start again but this time I needed to understand and
document the model up front and that I needed to produce a storyboard to have a
planned user interface. The entity diagram for the database design and object models
for the Family Scrapbook application can be found in Appendix A and the storyboards
for the main web pages can be found in Appendix B.
For quite some time during the second iteration of the project I was still struggling
with the implementation of objects and understanding the syntax of RoR but at some
point in mid-October I realised that I has become very comfortable with development
environment and that the application was starting to take shape.
PHP has until the latest version been a transactional language but version 5 introduces
the ability to produce object constructs with many of the capabilities of an object-
oriented language while retaining the ability to be used as a purely transactional
language.
Page 54 of 126
justfile.php photo.php
Skills Used
Allows users to browse, select images to upload and display the uploaded images. The
files are stored on the web server. The OO approach greatly reduces the workload and
complexity of the harness program called initially by the “Submit” button. The harness
creates an instance of the photo object with the details of the image file. All control of the
status, error checking and the ability to affect the image file are controlled by the object
and it‟s associated method calls. The harness program simply executes some method calls
to the object but has no need to know the state of the image file, this is in the object.
The harness makes use of PHP‟s built in image manipulation libraries. The page is
redrawn after upload as AJAX is not implemented.
None.
Page 56 of 126
process_form.php locations.php
Skills Used
This example implements the GoogleMaps API, this API is an object-oriented piece of
code that extends the basic XHR object to allow the placement of information icons and
overlay routes onto a map.
This demonstrates the practical implementation of the Google Maps API to embed a
mashup within a web application. Using the information entered into the “Add a new
location” form the web page takes a number of actions”:
It validates the input fields using Javascript, generates Latitude and Longitude co-
ordinates using the API, an XHR Object is created in a Javascript function, the XHR
Object is then passed a Universal Resource Locator (URL) containing the script to be
running and a set of variables to be POSTed. The new entry is updated on the map.
Mashup.
Page 57 of 126
_list_item.rhtml
Skills used
Installed and used „mini-magick‟ gem to manipulate images before storage on server.
application.js
Skills used
Integrated GoogleMaps using GoogleMaps API to create and display location attributes
in the scrapbook model.
Mashups
Sprints 0.2 through to 0.4 provide evidence that identical functionality and capabilities
are available in both PHP and RoR. The table in figure 15 confirms that the use of the
RoR MVC framework greatly reduces the amount of code that needs to be written to
achieve the same task.
The MVC framework of RoR adds control, structure and clarity to the development of
the example web applications. In PHP the application was a selection of randomly named
files in a directory while the RoR file location and naming conform to the conventions
required to make best use of the framework.
The experiment in Sprint 0 appears to validate the claim of RoR to reduce the complexity
and time taken to produce web applications. For this reason the remainder of the practical
application will be developed using RoR.
Page 60 of 126
Skills used
Installed and used „mini-magick‟ gem to manipulate images before storage in image.
Used AJAX, implemented in create.rjs, to update the list of photos with new photo
highlighted without a page refresh.
Skills used
Created connection model with “join” properties between scrapbook and photo allowing
a many to many relationship between the scrapbook and photos while recording the
position and default photo attributes in the connection model.
Reused add photo method from the photo model and view.
Used AJAX, implemented in create.rjs, to update scrapbook when photos are added or
made default.
Skills used
Created slider collection of photos in scrapbook and displayed tiny images of photos in
collection as selection menu.
Update main photo from selection on slider collection or use of next and previous arrows.
When in existence, shopping cart object and its attributes are displayed with control
buttons.
Skills used
Created tag methods to locate related (tagged) photos and related (tagged photos other
tags) tags.
Tagging
Page 64 of 126
Skills used
NOTE: Sprint 5.3: Implement family groups based access has not yet been
implemented due to time constraints. This will be added if there is sufficient time before
the submission date. About 8 hours work is required to implement and test this facility.
Page 65 of 126
Skills used
Integrated GoogleMaps using GoogleMaps API to create and display location attributes
in the scrapbook model.
Updated home view to allow navigation to scrapbooks via icon or location on Google
map.
Mashups
Before Save
After Save
application.js
Skills used
Wrote JavaScript methods in Prototype and Script.aculo.us. This allows drag and drop
sorting on the screen. When save button is pressed the new order is saved in the model,
the default photo updated and the scrapbook redrawn using Ajax
Skills used
Implemented basic blogging interface. 8 hours work required to add to scrapbooks and
test.
Blogging
Syndication
NOTE: Sprint 8.3: Add Web Service interface has not yet been implemented due to
time constraints. This will be added if there is sufficient time before the submission date.
About 12 hours work is required to implement and test this facility.
Page 68 of 126
I feel the major lessons, in terms of a technology discussion, I have learnt are as
follows.
As detailed in section 4.3.1, I had far greater difficulty than I had anticipated
applying my basic RoR and Agile skills on the practical application. It took around
three and a half months of work for me to become confident in my abilities to write
in RoR.
Design is all, despite what you are told in numerous RoR books, blogs and websites.
Not having a well defined Entity Model that mapped onto a relationship diagram
caused untold problems and resulted in a complete rewrite from scratch of the
application. Yes, you can bolt on extra pieces to the Rails application but the core
Page 69 of 126
Objects must be designed and their key methods defined before coding starts. Sorry
DHH, you are wrong.
Rails provides some amazing features in the framework, I just wish I knew what they
all were. People have produced some amazing work to provide plugins for Rails that
make the application environment a wonderful place, „acts_as_taggable‟,
„attachment_fu‟ and „mini-magick‟ are three that spring to mind. It does highlight the
fact that RoR is still to mature as a framework as so many seemingly core facilities
are still lacking. However all these plugins suffer from extremely limited and
disjointed documentation.
However, I have to add a big thank you to many contributors in the RoR
community. I am pleasantly surprised and impressed by the attitude and knowledge
of the contributors to the many forums and blogs.
Page 70 of 126
The consistency of the MVC meant that it was a clear decision where the next part of
the application would be implemented and the access to information through the
previously prepared object methods provided fast development using already tested
components.
I fully expected the „sortable scrapbook‟ to be one of the hardest features to create
but the existence of object methods to do the reordering and redisplay of information
meant that the major work was the development of a Ajax JavaScript function. A
function I might add that is not available through the Prototype libraries.
I believe there are numerous reasons why it took this long to grasp the more complex
issues of RoR but I feel that the main two reasons that it took this time for me were:
Identified and discussed, through academic research, the changes in the World
Wide Web, particularly the move from Web 1.0 to Web 2.0.
Discussed in detail the characteristics of Web 2.0 type web sites and applications.
An electronic copy of this dissertation and access to the practical application are
available at http://www.shearan.com/dissertation/
The research paper alone could have been a dissertation and I was forced to reduce
and heavily edit the theoretical content.
Learning a new language and development style from scratch was extremely time
consuming and the project may have benefited more if the development had been
undertaken in CakePHP or Apache Tomcat as I have a working knowledge of both
PHP and Java.
Page 72 of 126
However, I am satisfied that I have been able to achieve the majority of the goals I set
out to meet.
I found the experience extremely satisfying, as I was able to write a solution of far
more complexity than I expected to be possible during the project.
This means that web application developers will need to dedicate an extended period
of time, possibly about two months, to learn the skills required to produce a RoR
application BUT I believe that the learning curve flattens out quickly and the
developer will be able to implement Web 2.0 applications in a shorter timescale than
with the existing market dominant tools of PHP and ASP.
5.5 FOLLOW UP
There are a number of other Object Oriented MVC frameworks currently available,
including Apache Tomcat (and other Java based Web MVCs), Django and CakePHP.
Page 73 of 126
References
1
O‟Reilly, T. (2005): What is Web 2.0: Design Patterns and Business Models for the
Next Generation of Software. Retrieved September 17, 2007 from
http://www.oreilly.com/pub/a/oreilly/tim/news/2005/09/30/what-is-web-20.html
2
Berners-Lee,T. (1999): Weaving the Web: Origins and Future of the World Wide
Web. US: Texere
3
O‟Reilly, T. (2006): Web 2.0 Compact Definition: Trying Again. Retrieved April 26,
2007 from http://radar.oreilly.com/archives/2006/12/web_20_compact.html
4
Hinchcliffe, D. (2006): Best (or Most Interesting) Web 2.0 Definitions and
Explanations. Retrieved September 16, 2007 from
http://web2.socialcomputingmagazine.com/review_of_the_years_best_web_20_
explanations.htm
5
Garrett, J J. (2005): Ajax: A new Approach to Web Applications. Retrieved 21 April,
2007 from
http://www.adaptivepath.com/publications/essays/archives/000385.php
6
Shannon, R. (2006): Frames: Good or Bad. Retrieved January 5, 2007 from
http://www.yourhtmlsource.com/frames/goodorbad.html
7
Fielding, R T. (2000): Architectural Styles and the Design of Network-based Software
Architectures, PhD thesis, UC Irvine. Retrieved 22 April, 2007 from
http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm
8
Metz, C (2007): Web 3.0 Semantic Web. PC Magazine March, 2007. Retrieved March
23, 2007 from http://www.pcmag.com/article2/0,1895,2102852,00.asp
9
Schreiter, T. (2007): An Introduction into Semantic Web Services. University of
Potsdam, Germany.
10
Paolucci, M., Sycara, K. and Kawamura, T. (2003): Delivering Semantic Web
Services. Tech. report CMU-RI-TR-02-32, Robotics Institute, Carnegie Mellon
University, May, 2003
11
Nykanen, O. (2002): W3C FI & W3C Semantic Web. Retrieved March 12, 2007 from
http://www.w3c.tut.fi/talks/2003/0331umedia-on/slide6-0.html
Page 75 of 126
12
Djeraba, C., Sebe, N., and Lew, M.S. (2005): Systems and architectures for
multimedia information retrieval. Multimedia Systems Volume 6, Number 10,
October 2005, Pages 457-463.
13
Seguy, D. (2007): PHP Statistics For March 2007. Nexen.net. Retrieved April 24,
2007 from http://www.nexen.net/chiffres_cles/phpversion/16811-
php_statistics_for_march_2007.php
14
Paulson, D P. (2007). Developers Shift to Dynamic Programming Languages.
Computer Volume 40, Number 2, February 2007, Pages 12-15.
15
Anon. (2007): Model-view-controller. Retrieved 20 April, 2007 from
http://en.wikipedia.org/wiki/Model-view-controller
16
Macaulay, M. (2007): PHP with MySQL Coursenotes, Web Database for Multimedia,
LSBU, Week 6, Semester 2 2006/7.
17
Cong, Y. and Du, H. (2007): Welcome to the World of Web 2.0. The CPA Journal,
May 2007, Pages 6-10.
18
Musser, J. with O‟Reilly, T. (2006): Web 2.0 Principles and Best Practices. US:
O‟Reilly Media Inc.
19
O‟Reilly, T. (2006): Levels of the Game: The Hierarchy of Web 2.0 Applications.
Retrieved September 17, 2007 from
http://radar.oreilly.com/archives/2006/07/levels_of_the_game.html
20
Marshall, K. (2006): Web Services on Rails. US: O‟Reilly Media Inc.
21
Raymond, S. (2007): Ajax on Rails. US: O‟Reilly Media Inc.
22
Anon, LightReading.com (2002): DOM – Document Object Model Retrieved
September 25, 2007 from http://www.lightreading.com/techenc.asp?term=DOM
23
Anon(2007): Elevator Pitch. Retrieved October 24, 2007 form
http://en.wikipedia.org/wiki/Elevator_pitch
24
Batchelder, M. (2006): Ajax; Templating; and the Separation of Layout Logic.
Retrieved October 24, 2007 from http://borkweb.com/story/tag/diagram
25
Guzewich, T., Kent, M., Pfieffer, A., and Shank, K. (2006): Software Architecture
Case Study: Ruby on Rails.
26
Anon. (2007): Introduction to CakePHP. Retrieved September 25, 2007 from
http://manual.cakephp.org/chapter/intro
27
Thomas, D. and Heinemeier-Hansson, D. (2007): Agile Web Development with Rails.
US: Pragmatic Bookshelf.
Page 76 of 126
28
Anon. (2004): About Ruby. Retrieved from September 7, 2007 from http://www.ruby-
lang.org/en/about/
29
Raymond, S. (2007). Ajax on Rails. US: O‟Reilly Media, Inc.
30
Hellsten, C. and Laine, J. (2006): Beginning Ruby on Rails E-Commerce, From
Novice to Professional. New York: Apress.
31
Beck, K. (1999) Extreme Programming Explained: Embrace Change. New York,
Addison-Wesley.
Biblography
Babin, L. (2007): Beginning Ajax with PHP, From Novice to Professional. New York:
Apress.
Page 77 of 126
Welling, L and Thomson, L. (2005): PHP and MySQL Web Development, 3rd Edition.
Indiana: Developer‟s Library.
Darie, C., Brinzarae, B., Bucica, M. and Chereches-Tosa, F. (2006): Ajax and PHP,
Building Responsive Web Applications. Birmingham, UK: Packt Publishing.
Lewis, A., Purvis, M., Sambells, J and Turner, C.(2007): Google Maps Applications with
Rails and Ajax. From Novice to Professional. New York: Apress.
Page 78 of 126
1
user_id :int * default_photo :T/F
* photo_id :int
parent_id :int K ey
P hotos - pictur es
thumbnail
createdat
:string
:datetime
* photo_id :int
1
1 1
* One to many relationships
Slider Page
ScrapbookT itl e
Scrapbook T itle Shows no. of times
Logo Vis iting Info visited this page
illustrating successful
use of ajax
Thumbnails
Toggles to show
tiny related photos
M ain Photo
R elated Tags
SHOPPING
Ajax updates Tags
Visible if exisit s
CART
Description
Add to shopping cart
Scrapbook view
ScrapbookT itl e
Scrapbook T itle
Logo Vis iting Info
Q u ic k T im e ™ a n d a
G r a p hic s d e c o m p r e s s o r
a r e e e d
n e d t o s e e t h i
s p i
c t u r e .
Q u ic k T im e ™ a n d a
G r a p hic s d e c o m p r e s s o r
a r e e e d
n e d t o s e e t h i
s p i
c t u r e .
Q u ic k Tim e ™ a n d a
G r a p hic s d e c o m p r e s s o r
a r e e e d e
n d t o s e e t h is p ic t u r e .
Icons of Default
Photo for scrapbook
Nam e Nam e Nam e Nam e Nam e Nam e
L in k
Naviga
t e t o sc
r apbookvia pinhea
d or icon
Page 80 of 126
Current photos in
scrapbook
D efault Photo
Edit buttons:-
Rotate Left / Right
M ENU Make Default
Delete photo
save
Upload photos to
scrapbook
def initialize
@items = []
end
def add_hardCopy(photo)
current_item = @items.find{|item| item.photo == photo }
if current_item
current_item.increment_quantity
else
current_item = CartItem.new(photo)
@items << current_item
end
current_item
end
def remove_product(photo)
current_item = @items.find {|item| item.photo == photo}
current_item.decrement_quantity
if current_item.quantity == 0
@items.delete(current_item)
end
current_item
end
def total_items
@items.sum { |item| item.quantity}
end
def total_quantity
@items.sum{ |item| item.quantity }
end
end
def initialize(photo)
@photo = photo
@quantity = 1
end
def increment_quantity
@quantity += 1
end
Page 82 of 126
def decrement_quantity
@quantity -= 1
end
def name
@photo.name
end
def title
@photo.description
end
def size
@photo.origSize
end
def thumb
thumb = photo.pictures.first.public_filename(:thumb)
end
end
has_and_belongs_to_many :pictures
has_many :connections
has_many :scrapbooks, :through => :connections
acts_as_taggable
validates_presence_of :name
def name_desc
"#{name} #{description}"
end
def add_photo_with_pictures(params)
picture = Picture.create(params[:picture])
photo = Photo.new(params[:photo])
photo.name = File.basename(picture.filename,".*")
photo.date = Time.now
piccies = Picture.find_by_sql("select * from pictures where filename like '#{photo.name}%'")
photo.owner_id = piccies.size
photo.save
Page 83 of 126
piccies.each do |piccie|
photo.pictures << piccie
end
photo
end
end
has_and_belongs_to_many :photos
has_attachment :storage => :file_system,
:max_size => 4.megabytes,
:thumbnails => { :large => '600x600', :medium => '480x480>',
:thumb => '100x100!', :tiny => '40x40>' },
:processor => :MiniMagick
validates_as_attachment
def rotate_image(picture,direction)
types = ["","large","medium","thumb","tiny"]
if direction == 'left'
degrees = "-90"
else
degrees = "90"
end
for type in types do
image = MiniMagick::Image.from_file("#{RAILS_ROOT}/public"+picture.public_filename(type))
image.rotate(degrees)
image.write("#{RAILS_ROOT}/public"+picture.public_filename(type))
end
end
end
validates_presence_of :name
def self.find_not_in_scrapbook(target_scrapbook)
in_scrapbook = target_scrapbook.photos
not_in_scrapbook = Array.new
all_photos = Photo.find(:all)
not_in_scrapbook = all_photos - in_scrapbook
Page 84 of 126
not_in_scrapbook
end
def self.find_all_except_default
find_by_sql("select * from scrapbooks where name not like 'default%'")
end
def self.find_with_marker
find_by_sql("select * from scrapbooks where lat is NOT NULL and lng is NOT NULL")
end
end
attr_accessor :password_confirmation
validates_confirmation_of :password
def validate
errors.add_to_base("Missing Password") if hashed_password.blank?
end
def after_destroy
if User.count.zero?
raise "You can't delete the last user!!!"
end
end
def password
@password
end
def password=(pwd)
@password=pwd
return if pwd.blank?
Page 85 of 126
create_new_salt
self.hashed_password = User.encrypted_password(self.password, self.salt)
end
private
def create_new_salt
self.salt = self.object_id.to_s + rand.to_s
end
end
VIEWS
***** /admin/photo/_form.rhtml *****
<table>
<tr>
<td>
<dl>
<dt>ID</dt><dd><%= @photo.id%></dd>
<dt>Name</dt><dd><%= @photo.name %></dd>
<dt>Description</dt><dd><%= @photo.description%></dd>
<dt>Owner ID</dt><dd><%= @photo.owner_id%></dd>
<dt>Date</dt><dd><%= @photo.date%></dd>
<dt>Aspect</dt><dd><%= @photo.aspect%></dd>
<dt>Tags</dt><dd><%= display_tags_in_link @photo%></dd>
</dl>
</td>
<td>
<%= image_tag(@photo.pictures.first.public_filename(:medium)) %>
</td>
</tr>
</table>
<h4>Tags</h4>
<% for tag in Photo.find_related_tags(@photo.tags.collect(&:name), :separator => ',', :raw =>
true, :limit => 100)%>
<%= link_to tag['name'], :controller => '/tag', :action => 'show', :id => tag['name']%><br
/>
<% end %>
</div>
<% end %>
Page 86 of 126
<p><%= link_to 'Edit', :action => 'edit', :id => @photo %> | <%= link_to 'Back', :action => 'index' %></p>
<%= link_to 'Show', {:action => 'show', :id => @photo.id }%>|
<%= link_to 'Back', {:action => 'index'} %>
</p>
<% end %>
<iframe id='upload_frame' name="upload_frame" style="width:1px;height:1px;border:0px"
src="about:blank"></iframe>
Page 88 of 126
<h4>Tags</h4>
<% for tag in Photo.find_related_tags(@photo.tags.collect(&:name), :separator => ',', :raw =>
true, :limit => 100)%>
<%= link_to tag['name'], :controller => '/tag', :action => 'show', :id => tag['name']%><br
/>
<% end %>
</div>
<% end %>
Page 89 of 126
<p><%= link_to 'Edit', :action => 'edit', :id => @photo %> | <%= link_to 'Back', :action => 'index' %></p>
</p>
<% end %>
<iframe id='upload_frame' name="upload_frame" style="width:1px;height:1px;border:0px"
src="about:blank"></iframe>
page.form.reset "add_scrapbook"
***** /admin/scrapbook/create.rjs *****
page.insert_html :bottom, "scrapbooks", :partial => 'list_item', :object => @scrapbook
page.visual_effect :highlight, "scrapbook_#{@scrapbook.id}"
page.form.reset "add_scrapbook"
</div>
<%end %>
<% @scrapbooks.each do |scrapbook| %>
<%= render(:partial => 'list_item', :object => scrapbook) %>
<% end %>
<% if @scrapbook_pages.current.next %>
<div class="thumb">
<%= link_to("Next page", { :page => @scrapbook_pages.current.next }) %>
</div>
<%end %>
</div>
<div class="depot-peterform">
<fieldset>
<legend>Create a new scrapbook</legend>
<div id="map" class="mediummap"></div>
</fieldset>
</div>
***** /feed/photos.rxml
xml.instruct!
xml.rss "version" => "2.0", "xmlns:dc" => "http://purl.org/dc/elements/1.1" do
xml.channel do
xml.title 'Family Scrapbook'
xml.link url_for(:only_path => false, :controller => 'library', :action => 'index' )
xml.pubDate CGI.rfc1123_date(@photos.first.date)
xml.description h("New Photos from the Family Scrapbook")
@photos.each do |photo|
xml.item do
xml.title photo.name
xml.link url_for(:only_path => false, :controller => 'library', :action => 'show_photo', :id => photo )
xml.description h(photo.description)
xml.pubDate CGI.rfc1123_date(photo.date)
xml.guid url_for(:only_path => false, :controller => 'library', :action => 'show_photo', :id => photo )
xml.image image_tag(photo.pictures.first.public_filename(:tiny))
end
end
end
end
</head>
<body id="screen">
<div id="columns">
<div id="admin_user">
<% if @user %>
<h4>user id : <%= @user.name %></h4>
<%= render(:partial => "library/admin_menu")%>
<% else %>
<%= render(:partial => "library/user_menu")%>
<% end %>
</div>
<div id="shop">
<%= hidden_div_if(@cart.items.empty?, :id => "cart") %>
<%= render(:partial => "library/cart", :object =>
@cart)%></div>
</div>
</div>
<div id="main">
<%if flash[:notice] -%>
<div id="notice"><%= flash[:notice ]%></div>
<% end -%>
<%= yield :layout %>
</div>
</div>
</body>
</html
CONTROLLERS
***** /admin/photo_controller.rb *****
class Admin::PhotoController < ApplicationController
before_filter :authorize
def new
@photo = Photo.new
@page_title = 'Create new photo'
end
Page 94 of 126
def create_ajax
responds_to_parent do
render :update do |page|
page << "alert($('stuff').innerHTML)"
page.visual_effect :highlight, "stuff"
end
end
end
def create
begin
photo = Photo.new
@new_photo = photo.add_photo_with_pictures(params)
@new_photo.tag(params[:tags], :separator => ',')
scrapbook = Scrapbook.find(params[:scrapbook])
@new_photo.scrapbooks << scrapbook
rescue
redirect_to_index("Failed to create the picture - do it properly")
else
responds_to_parent do
render :update do |page|
page.insert_html :bottom, "photos", :partial => 'list_item', :object => @new_photo
page.visual_effect :highlight, "picture_#{@new_photo.id}"
page.form.reset "add_photo"
end
end
end
end
def edit
set_up
@photo = Photo.find(params[:id])
@page_title = 'Edit Photo Information'
end
def update
@photo = Photo.find(params[:id])
@photo.tag(params[:tags], :separator => ',', :clear => true)
if @photo.update_attributes(params[:photo])
flash[:notice] = "Photo #{@photo.description} was successfully updated"
redirect_to :action => 'index'
else
@page_title = 'Edit Photo Information'
render :action => 'edit'
end
end
def destroy
@photo = Photo.find(params[:id])
for connection in @photo.connections
connection.destroy
end
@photo.destroy
Page 95 of 126
end
def show
set_up
@photo = Photo.find(params[:id])
@page_title = @photo.name
end
def index
set_up
@photo_pages, @photos = paginate :photos, :per_page => 18
# @photos = Photo.find(:all)
@page_title = "Listing All Photos"
@scrapbooks = Scrapbook.find(:all, :order => 'name')
end
def get_time
sleep 1.second
render :text => Time.now.strftime("%d-%m-%Y %H:%M:%S")
end
def spin_left
photo = Photo.find(params[:id])
spin_photo(photo,'left')
end
def spin_right
photo = Photo.find(params[:id])
spin_photo(photo,'right')
end
def spin_photo(photo,direction)
picture = photo.pictures.first
picture.rotate_image(picture,direction)
render :update do |page|
page["picture_#{photo.id}"].replace :partial => "list_item", :object => photo
page["picture_#{photo.id}"].visual_effect :highlight
end
end
private
<table><tr>
<% if p_photie %>
<td>
<%= form_remote_tag :url => {:action =>'update_slider', :id => p_photie, :scrapbook =>
@scrapbook } %>
<%= image_submit_tag("/images/bwdarrow.jpg") %>
<%= end_form_tag %>
</td>
<% else %>
<td><img src="/images/space.jpg" /></td>
<% end %>
<td class="mainpic" align="center">
<%= image_tag(main_picture.pictures.first.public_filename(:medium)) %>
</td>
<h3>Related Photos</h3>
<% if main_picture.tagged_related.size == 0%>
No Similarly tagged photos
<% end%>
<% for photo in main_picture.tagged_related %>
<%= form_remote_tag :url => {:action =>'update_slider', :id => photo,
:scrapbook => @scrapbook } %>
<%= image_submit_tag(photo.pictures.first.public_filename(:tiny)) %>
<%= end_form_tag %>
Page 98 of 126
<h3>Related Tags</h3>
<% for tag in Photo.find_related_tags(main_picture.tags.collect(&:name), :separator =>
',', :raw => true, :limit => 100)%>
<%= link_to_remote tag['name'], :url => {:action => 'toggle', :tag_name =>
tag['name']}%><br />
<div id="related_<%= tag['name']%>" style="display:none">
<% @tagged_list = Photo.find_tagged_with(:any => tag['name'], :separator =>
',') %>
<% for photo in @tagged_list %>
<%= form_remote_tag :url => {:action =>'update_slider', :id => photo,
:scrapbook => @scrapbook } %>
<%=
image_submit_tag(photo.pictures.first.public_filename(:tiny)) %>
<%= end_form_tag %>
<% end %>
</div>
<% end %>
</div>
def index
set_up
@scrapbook_pages, @scrapbooks = paginate :scrapbooks, :per_page => 4
@page_title = "Listing All Scrapbooks"
end
def create
begin
photo = Photo.new
@new_photo = photo.add_photo_with_pictures(params)
@scrapbook = Scrapbook.find(params[:scrapbook])
Page 100 of 126
def destroy
@scrapbook= Scrapbook.find(params[:id])
for connection in @scrapbook.connections
connection.destroy
end
@scrapbook.destroy
end
def add_photos
set_up
@scrapbook = Scrapbook.find(params[:id])
@not_in_scrapbook =Scrapbook.find_not_in_scrapbook(@scrapbook)
@page_title = "Add photos to #{@scrapbook.name} Scrapbook"
end
def add_photos_to_scrapbook
@scrapbook = Scrapbook.find(params[:id])
@updated_photos = Array.new
for photo_id in params[:photo_ids]
@photo = Photo.find(photo_id)
@scrapbook.photos << @photo
@updated_photos << @photo
end
@not_in_scrapbook =Scrapbook.find_not_in_scrapbook(@scrapbook)
end
def destroy_connection
@photo = Photo.find(params[:id])
@scrapbook = Scrapbook.find(params[:scrapbook])
conn = Connection.find_by_scrapbook_id_and_photo_id(@scrapbook,@photo)
conn.destroy
@not_in_scrapbook =Scrapbook.find_not_in_scrapbook(@scrapbook)
end
def make_default_photo
@new_default_photo = Photo.find(params[:id])
@scrapbook = Scrapbook.find(params[:scrapbook])
if @scrapbook.default_photos.size > 0
for photo in @scrapbook.default_photos do
set_connection(@scrapbook,photo,false)
Page 101 of 126
end
end
set_connection(@scrapbook,@new_default_photo,true)
@scrapbook.reload
@not_in_scrapbook =Scrapbook.find_not_in_scrapbook(@scrapbook)
redirect_to :action => 'add_photos', :id => scrapbook.id unless request.xhr?
end
def spin_left
@scrapbook = Scrapbook.find(params[:scrapbook])
photo = Photo.find(params[:id])
spin_photo(photo,'left')
end
def spin_right
@scrapbook = Scrapbook.find(params[:scrapbook])
photo = Photo.find(params[:id])
spin_photo(photo,'right')
end
def spin_photo(photo,direction)
picture = photo.pictures.first
picture.rotate_image(picture,direction)
render :update do |page|
page["photo_#{photo.id}"].replace :partial => "current_photo", :object => photo
page["photo_#{photo.id}"].visual_effect :highlight
end
end
def resort_scrapbook
@scrapbook = Scrapbook.find(params[:scrapbook])
@scrapbook_sortable = params[:scrapbook_sortable]
for photo_id in @scrapbook_sortable.reverse
photo = Photo.find(photo_id)
conn = Connection.find_by_scrapbook_id_and_photo_id(@scrapbook,photo)
conn.move_to_top
end
default_photo = Photo.find( @scrapbook_sortable[0])
if @scrapbook.default_photos.size > 0
for photo in @scrapbook.default_photos do
set_connection(@scrapbook,photo,false)
end
end
set_connection(@scrapbook,default_photo,true)
@scrapbook.reload
end
def set_connection(scrapbook,photo,state)
conn = Connection.find_by_scrapbook_id_and_photo_id(scrapbook,photo)
conn.default_photo = state
conn.move_to_top if state
conn.save
end
end
Page 102 of 126
private
def authorize
unless User.find_by_id(session[:user_id])
session[:original_uri] = request.request_uri
flash[:notice] = "Please Log In"
redirect_to(:controller => "/login", :action => "login")
end
end
def find_cart
session[:cart] ||= Cart.new
end
end
def create
begin
scrapbook = Scrapbook.create(params[:s])
scrapbook.create_date = Time.now
if scrapbook.save
res = {:success => true, :content =>
"<div><strong>Name: </strong>#{scrapbook.name}</div><div><strong>Description:
</strong>#{scrapbook.description}</div>", :icon=>scrapbook.icon}
else
res = {:success => false,:content => "Could not save the marker"}
end
Page 103 of 126
rescue
res = {:success => false,:content => "Went pear shaped"}
render :text => res.to_json
else
render :text => res.to_json
end
end
def list
render :text => Scrapbook.find_with_marker.to_json
end
end
def index
set_up
@scrapbooks = Scrapbook.find_all_except_default
@count = increment_count
end
def show
set_up
@scrapbook = Scrapbook.find(params[:id])
@page_title = "#{@scrapbook.name} Scrapbook"
end
def slider
set_up
@scrapbook = Scrapbook.find(params[:scrapbook])
@photo = Photo.find(params[:id])
@page_title = "#{@scrapbook.name} Scrapbook"
end
def update_slider
begin
@photo = Photo.find(params[:id])
@scrapbook = Scrapbook.find(params[:scrapbook])
rescue
logger.error("Attempt to access invalid photo #{params[:id]}")
redirect_to_index("Invalid Photo, ID passed: #{params[:id]}")
else
redirect_to_index unless request.xhr?
end
end
def checkout
set_up
end
def toggle
@toggle = params[:tag_name]
end
Page 104 of 126
def add_to_cart
begin
photo = Photo.find(params[:id])
rescue ActiveRecord::RecordNotFound
logger.error("Attempt to access invalid photo #{params[:id]}")
redirect_to_index("Invalid Photo, ID passed: #{params[:id]}")
else
@cart = find_cart
@current_item = @cart.add_hardCopy(photo)
redirect_to_index unless request.xhr?
end
end
def remove_from_cart
begin
@cart = find_cart
photo = Photo.find(params[:id])
rescue ActiveRecord::RecordNotFound
logger.error("Attempt to access invalid photo #{params[:id]}")
redirect_to_index("Invalid product" )
else
@current_item = @cart.remove_product(photo)
redirect_to_index unless request.xhr?
end
end
def empty_cart
session[:cart] = nil
redirect_to_index unless request.xhr?
end
private
def find_cart
session[:cart] ||= Cart.new
end
def increment_count
session[:counter] ||= 0
session[:counter] += 1
end
end
def add_users
set_up
@user = User.new(params[:user])
Page 105 of 126
@user.save
@all_users = User.find(:all)
end
def login
set_up
session[:user_id] = nil
if request.post?
user = User.authenticate(params[:name], params[:password])
if user
session[:user_id] = user.id
uri = session[:original_uri]
session[:original_uri] = nil
redirect_to (uri || {:controller => 'library', :action => 'index'})
else
flash[:notice] = "Invalid username/password combination"
end
end
end
def logout
session[:user_id] = nil
flash[:notice] = "Logged Out"
redirect_to :controller => '/library', :action => 'index'
end
def index
set_up
@total_photos = Photo.count
@total_scrapbooks = Scrapbook.count
end
def delete_users
if request.post?
user = User.find(params[:id])
begin
user.destroy
rescue Exception => e
flash[:notice] = e.message
end
@all_users = User.find(:all)
end
end
def list_users
set_up
@page_title = "Listing All Users"
@all_users = User.find(:all)
end
end
def index
@pictures = Picture.find(:all)
respond_to do |format|
format.html # index.rhtml
format.xml { render :xml => @pictures.to_xml }
end
end
# GET /pictures/1
# GET /pictures/1.xml
def show
@picture = Picture.find(params[:id])
respond_to do |format|
format.html # show.rhtml
format.xml { render :xml => @picture.to_xml }
end
end
# GET /pictures/new
def new
@picture = Picture.new
end
# GET /pictures/1;edit
def edit
@picture = Picture.find(params[:id])
end
# POST /pictures
# POST /pictures.xml
def create
@picture = Picture.new(params[:picture])
respond_to do |format|
if @picture.save
flash[:notice] = 'Picture was successfully created.'
format.html { redirect_to picture_url(@picture) }
format.xml { head :created, :location => picture_url(@picture) }
else
format.html { render :action => "new" }
format.xml { render :xml => @picture.errors.to_xml }
end
end
end
# PUT /pictures/1
# PUT /pictures/1.xml
def update
@picture = Picture.find(params[:id])
respond_to do |format|
if @picture.update_attributes(params[:picture])
flash[:notice] = 'Picture was successfully updated.'
Page 107 of 126
# DELETE /pictures/1
# DELETE /pictures/1.xml
def destroy
@picture = Picture.find(params[:id])
@picture.destroy
respond_to do |format|
format.html { redirect_to pictures_url }
format.xml { head :ok }
end
end
end
def list
set_up
@posts = Post.find :all
end
def show
@post = Post.find(params[:id])
end
def new
@post = Post.new
end
def create
@post = Post.new(params[:post])
if @post.save
flash[:notice] = 'Post was successfully created.'
redirect_to :action => 'list'
else
render :action => 'new'
end
end
Page 108 of 126
def edit
@post = Post.find(params[:id])
end
def update
set_up
@post = Post.find(params[:id])
if @post.update_attributes(params[:post])
flash[:notice] = 'Post was successfully updated.'
redirect_to :action => 'show', :id => @post
else
render :action => 'edit'
end
end
def comments
set_up
@post = Post.find(params[:id])
if @post.comments.create(:body => params[:comment])
flash[:notice] = 'Comment was successfully added.'
else
flash[:notice] = 'Failed to add Comment.'
end
redirect_to :action => 'show', :id => @post
end
def destroy
Post.find(params[:id]).destroy
redirect_to :action => 'list'
end
end
def list
set_up
@page_title = 'Listing tags'
@tag_pages, @tags = paginate :tags, :order => :name, :per_page => 15
end
def show
set_up
tag = params[:id]
@page_title = "Photos tagged with #{tag}"
@photos = Photo.find_tagged_with(:any => tag, :separator => ',')
end
end
MISCELLANEOUS
***** /public/javascripts/application.js *****
var centerLatitude = 51.465471;
var centerLongitude = -0.143320;
Page 109 of 126
function init() {
if (document.getElementById("map")) {
drawMap();
}
}
function drawMap()
{
map2 = document.getElementById("map2");
if (GBrowserIsCompatible()) {
if (map2) {
startZoom = 2;
} else {
startZoom =13;
}
map = new GMap2(document.getElementById("map"));
listMarkers();
map.addControl(new GMapTypeControl());
map.addControl(new GSmallMapControl());
map.setCenter(new GLatLng(centerLatitude, centerLongitude), startZoom);
if (!map2) {
GEvent.addListener(map,"click",function(overlay,latlng){
var inputForm = document.createElement("form");
inputForm.id='geocache-input';
inputForm.setAttribute("action","");
inputForm.onsubmit = function() {createMarkerInPrototype(); return false;};
map.openInfoWindow(latlng,inputForm);
});
}
}
}
Page 110 of 126
function createMarkerInPrototype() {
var lng = $("longitude").value;
var lat = $("latitude").value;
var formValues=Form.serialize('geocache-input');
new Ajax.Request('/google/create',
{
method: 'post',
parameters: formValues,
onComplete: function(request) {
res=eval( "(" + request.responseText + ")" );
if(!res.success) {
alert(res.content);
} else {
var latlng = new GLatLng(parseFloat(lat),parseFloat(lng));
var marker = addMarkerToMap(latlng, res.content, res.icon);
map.addOverlay(marker);
map.closeInfoWindow();
}
}
});
}
} else {
alert("didn't get in");
var marker = new GMarker(latlng);
}
GEvent.addListener(marker, 'click', function() {
var markerHTML = html;
marker.openInfoWindowHtml(markerHTML)
});
return marker;
}
function listMarkers ()
{
new Ajax.Request('/google/list',
{
method: 'post',
onComplete: function(request) {
markers=eval( "(" + request.responseText + ")" );
for (var i=0; i < markers.length; i++ ) {
var marker=markers[i].attributes;
Page 111 of 126
var lat=marker.lat;
var lng=marker.lng;
if (lat && lng) {
var latlng = new GLatLng(parseFloat(lat),parseFloat(lng));
var html = '<div><b>Name:</b> '
+ marker.name
+ '</div><div><b>Description:</b> '
+ marker.description
+ '</div><div><a href="/library/show/'
+marker.id
+'">Open Scrapbook</a></div>';
var iconImage = marker.icon;
var marker = addMarkerToMap(latlng, html, iconImage );
map.addOverlay(marker);
}
}
}
});
}
function updateScrapbookOrder()
{
var fred = Sortable.serialize("scrapbook_sortable");
var scrapbook =
document.getElementById("scrapbook_sortable").getAttribute('scrapbook');
var sendit = "scrapbook="+scrapbook+"&"+fred;
new Ajax.Request('/admin/scrapbook/resort_scrapbook',
{
method: 'post',
asynchronous:true,
evalScripts:true,
parameters: sendit
});
}
function addMarker(latitude,longtitude,description)
{
var marker = new GMarker(new GLatLng(latitude,longtitude));
GEvent.addListener(marker, 'click',
function() {
marker.openInfoWindowHtml(description)
}
);
map.addOverlay(marker)
}
function getGroupOrder() {
var sections = document.getElementsByClassName('scrapbook_sortable');
var alerttext = '';
sections.each(function(section) {
var sectionID = section.id;
var order = Sortable.serialize(sectionID);
alerttext += sectionID + ': ' + Sortable.sequence(section) + '\n';
});
alert(alerttext);
Page 112 of 126
return false;
}
window.onload=init;
window.unload=GUnload;
<?php
}
else
{
// validate form data
// Create the file object
$uploadFile = new JustFile($_FILES['file']);
// Set up temp array in case the response is the bad file type array
$joe = $uploadFile->mimeToWord();
$uploadFile->moveToGallery($_POST['gallery']);
// Create the tiny, thumb and web versions of the original photo
$uploadFile->makeWebTypes();
?>
Page 114 of 126
//The $ext is done in the object in mimeToWord...this will not work now
$newFileName = $id . $ext;
function __construct($file=false)
{
if ($file) {
$this->file = $file;
$this->tmpFile = $file['tmp_name'];
$this->realFile = $file['name'];
Page 115 of 126
} else {
echo "<p><b>PS-OB-JF-Co: No file name </b></p>";
}
$this->fileSize();
$this->mimeToWord();
}
function fileSize()
{
$size = filesize($this->tmpFile);
if ($this->file['size'] == 0) {
echo "<p><b>PS-OB-JF-Fs: Bad file size [$this->file['size']] bytes</b></p>";
return false;
break;
} else {
return $size;
}
}
function mimeToWord()
{
switch ($this->file['type']){
case "image/jpeg":
$tested = "JPEG";
$this->ext = ".jpg";
break;
case "image/pjpeg":
$tested = "JPEG";
$this->ext = ".jpg";
break;
case "image/gif":
$tested = "GIF";
$this->ext = ".gif";
break;
default:
$tested = array("Unknown",$this->file['type']);
}
return $tested;
}
function moveToGallery($galleryName) {
$gallery = new FileDir($galleryName);
if (!$gallery->dirExists()) {
// echo "<p><b>PS-OB-JF-Mtg1: Needs to create the gallery directory</b></p>";
$newDir = $gallery->createDir();
$moveIt = $this->moveFile($gallery);
if ($newDir && $moveIt) {
$this->moved = true;
// echo "<p><b>PS-OB-JF-Mtg2: Created the gallery directory and
moved the file</b></p>";
return true;
break;
}
} else {
// echo "<p><b>PS-OB-JF-Mtg3: Gallery directory already exists</b></p>";
Page 116 of 126
$moveIt = $this->moveFile($gallery);
if ($moveIt) {
$this->moved = true;
return true;
// echo "<p><b>PS-OB-JF-Mtg4: Moved the file</b></p>";
break;
}
}
echo "<p><b>PS-OB-JF-Mtg5: FAILED to create gallery directory OR move the
file</b></p>";
return false;
}
function moveFile($gallery) {
$this->destination = $gallery->fileDir.'/'.$this->realFile;
// echo "<p><b>This is destination [$this->destination]</b></p>";
if (move_uploaded_file($this->tmpFile, $this->destination)) {
return true;
break;
}
return false;
}
function makeWebTypes()
{
if ($this->moved)
{
$this->photo = new Photo($this->destination);
$this->photo->createWebPics();
} else {
echo "<p><b>PS-OB-JF-Mwp1: File needs to be moved first</b></p>";
}
return false;
}
function getFileName($type) {
if ($this->moved)
{
$getFile = $this->photo->newFileName($type);
return $getFile;
} else {
return "<p><b>PS-OB-JF-Nfn1: File needs to be moved first</b></p>";
}
}
function show() {
if ($this->moved) {
$this->photo->show();
}
}
}
?>
Page 117 of 126
function __construct($fileDir)
{
if ($fileDir == "")
{
$this->fileDir = "default";
} else {
$this->fileDir = $fileDir;
}
}
if(is_dir($dirFind)) {
// echo "<p><b>PS-OB-FD-De1: Directory $dirFind exists</b></p>";
return true;
break;
} else {
// echo "<p><b>PS-OB-FD-De2: Directory $dirFind does not exist</b></p>";
}
return false;
}
function dirPermiss($dirFind)
{
// display directory permissions
return false;
}
function dirList()
{
// list directory contents - TBC
return false;
}
Page 118 of 126
if ($this->dirExists($newDir))
{
return true;
} else {
return false;
}
}
}
?>
function __construct($photofile)
{
$this->photo = $photofile;
$this->dimensions();
}
Page 119 of 126
function convertPhotos($type,$wide,$high) {
$size = $this->calculateDimensions($this->photoOrig['w'],$this-
>photoOrig['h'],$wide,$high);
Page 120 of 126
$load = 'ImageCreateFromJpeg';
$save = 'ImageJpeg';
$newFile = $this->newFileName($type);
$srcPhoto = $load($this->photo);
$destPhoto = imageCreateTrueColor($size['w'],$size['h']);
imagecopyresampled($destPhoto,$srcPhoto,0,0,0,0,
$size['w'],$size['h'],$this->photoOrig['w'],$this->photoOrig['h']);
$save($destPhoto,$newFile);
}
function newFileName($type) {
if ($type == 'orig') {
$newFile = $this->photo;
} else {
$pathInfo = pathinfo($this->photo);
$newFile = $pathInfo['dirname'].'/'.$type.'/'.$pathInfo['filename'].'-
'.$type.'.'.$pathInfo['extension'];
}
return $newFile;
}
function calculateDimensions($width,$height,$maxWidth,$maxHeight) {
$ret = array('w' => $width, 'h' => $height);
$ratio = $width/$height;
if ( $width > $maxWidth || $height > $maxHeight) {
$ret['w'] = $maxWidth;
$ret['h'] = $ret['w']/$ratio;
function show()
{
$fs = $this->dispFileSize($this->origFileSize);
echo "This is the file [$this->photo] <br /> The photo is [$this->aspect] <br />
This is the original Width [".$this->photoOrig['w']."]<br />
This is the original Height [".$this->photoOrig['h']."]<br />
The photo type is [$this->photoType]<br />
The original file size is [$fs]<br />";
}
function __toString()
{
return var_export($this, true);
}
}
?>
<?php
if (isset($_GET['message'])) {
$message = trim(strip_tags(stripslashes($_GET['message'])));
} else {
$message = '';
}
?>
</head>
<body onload="init('map','message')">
<div id="main">
<div id="map"></div>
<div id="formwrapper">
<?php if (strlen($message) > 0) { ?>
<div id="messages">
<?php echo htmlentities($message); ?>
</div>
<?php } else { ?>
<div id="messages" style="display: none"></div>
<?php } ?>
</tr>
<tr>
<td>County:</td>
<td><input type="text" name="county"
maxlength="150" /></td>
</tr>
<tr>
<td>Postcode:</td>
<td><input type="text" name="postcode"
maxlength="150" /></td>
</tr>
<tr><td>
</td></tr>
<tr><td>
<input type="submit" value="Add Location" />
</td></tr>
<tr><td>
</td></tr>
<tr><td colspan="2">
<A HREF="http://nearby.org.uk"
TARGET="_blank">PostCode to co-ordinates at nearby.org.uk</A>
</td></tr>
</table>
</form>
</div>
</div>
</body>
</html>
require_once('dbconnector.php');
require_once('locations.php');
opendatabase();
$error = false;
$error = true;
$fred = $values[$field];
}
}
if ($error) {
$message = 'Error adding Location'.$fred;
} else {
$query = sprintf("insert into store (%s) values ('%s')", join(', ', array_keys($values)),join("',
'",$values));
mysql_query($query);
$message = 'Location has been added: '.$values['locname'];
}
if ($ajax) {
echo $message;
} else {
header('Location: mapit.php?message=' . urlencode($message));
exit;
}
?>
$GLOBALS['host'] = 'localhost';
$GLOBALS['user'] = 'apressauth';
$GLOBALS['pass'] = 'blahblah';
$GLOBALS['db'] = 'taskdb';
function opendatabase(){
$db = mysql_connect($GLOBALS['host'],$GLOBALS['user'],$GLOBALS['pass']);
try {
if (!$db){
$exceptionstring = "<em>Error connecting to database:</em> <br />";
$exceptionstring .= mysql_errno() . ": " . mysql_error();
throw new exception ($exceptionstring);
} else {
mysql_select_db ($GLOBALS['db'],$db);
}
return $db;
} catch (exception $e) {
echo $e->getmessage();
Page 124 of 126
die();
}
}
?>
public $locCode;
private $nearbyKey = '90b6be422e81fc';
private $uri = 'http://www.nearby.org.uk/api/convert.php';
public $lat;
public $long;
function __construct($postCode) {
$this->locCode = preg_replace('/\s+/','',$postCode);
$this->getLatLong();
}
function getLatLong() {
$URL = $this->createURL();
include("$URL$this->locCode");
$this->lat = $convert_output['ll-wgs84']['lat'];
$this->long = $convert_output['ll-wgs84']['long'];
}
function createURL() {
$URL = $this->uri.'?key='.$this->nearbyKey.'&output=php&p=';
return $URL;
}
}
?>
function trim(str) {
return str.replace(/^(\s+)?(\S*)(\s+)?$/, '$2');
}
function showMessage(msg) {
msgContainer = document.getElementById("messages");
Page 125 of 126
if (msg.length == 0)
msgContainer.style.display = 'none';
else {
msgContainer.innerHTML = msg;
msgContainer.style.display = 'block';
}
}
function loadmap() {
var map = new GMap(mapContainer);
map.addControl(new GMapTypeControl());
map.addControl(new GSmallMapControl());
map.centerAndZoom(new GPoint(mapLng, mapLat), mapZoom);
var request = GXmlHttp.create();
request.open('POST','location.php',true);
request.onreadystatechange = function() {
if (request.readyState == 4) {
var xmlDoc = request.responseXML;
var markers = xmlDoc.documentElement.getElementsByTagName("marker");
for (var i = 0; i < markers.length; i++ ) {
var point = new
GPoint(parseFloat(markers[i].getAttribute("longitude")),
parseFloat(markers[i].getAttribute("latitude")));
var theaddy = '<div class="location"><strong>'
+ markers[i].getAttribute('locname') +
'</strong><br />'
+ markers[i].getAttribute('address') + '<br
/>'
+ markers[i].getAttribute('city') + ','
+ markers[i].getAttribute('county') + '<br
/>'
+ markers[i].getAttribute('postcode')
+ '</div>';
var marker = createInfoMarker(point, theaddy);
map.addOverlay(marker);
}
Page 126 of 126
function submitForm(frm) {
// alert("This is frm["+frm+"]");
showMessage('');
var fields = {
locname : 'You must enter the location name',
address : 'You must enter a address',
city : 'You must enter the city',
county : 'You must enter a county',
postcode : 'You must enter a postcode'
};
var errors = [];
var values = 'ajax=1';
for (field in fields) {
val = frm[field].value;
if (trim(val).length == 0) {
errors[errors.length] = fields[field];
}
values += '&' + field + '=' + escape(val);
}
if (errors.length > 0 ) {
var errMsg = '<strong>The following errors have occured: </strong><br /><ul>\n';
for (var i = 0; i < errors.length; i++) {
errMsg += '<li>' + errors[i] + '</li>\n';
}
errMsg += '</ul>\n';
showMessage(errMsg);
return false;
}
mapContainer = document.getElementById("map");
mapContainer.innerHTML = "<b>Loading map....</b>";
// alert("This is values["+values+"]");
var xmlhttp = GXmlHttp.create();
xmlhttp.open("POST",frm.action, true);
xmlhttp.setRequestHeader("Content-Type","application/x-www-form-urlencoded;
charset=UTF-8");
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
showMessage(xmlhttp.responseText);
}
}
xmlhttp.send(values);
setTimeout("loadmap()",1000);
}