Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletter Hub
Free Learning
Arrow right icon
timer SALE ENDS IN
0 Days
:
00 Hours
:
00 Minutes
:
00 Seconds

How-To Tutorials - Web Design

132 Articles
article-image-start-ad-serving-openx
Packt
29 Mar 2010
3 min read
Save for later

Start Ad Serving with OpenX

Packt
29 Mar 2010
3 min read
Basic OpenX Ad serving steps diagram The following diagram shows the necessary steps for the completion of the serving an advertisement on a website using OpenX Ad Server: Sample Amazon.com banner serving flowchart In this scenario, we will start adding an Advertiser (Amazon). Then, we will create a Campaign (Amazon Toys & Games). We will add a Banner (Amazon Puzzle Games for Kids) to this campaign. Then, we will define our sample website at OpenX. We will create a zone (Toys & Games Zone) for this website. The next step is to link a banner to this zone. Finally, we will complete serving advertisements by embedding the zone code to a page on the website and visiting this page through a browser. Time for action – adding Amazon.com as an advertiser In this section, we will learn how to add Amazon.com as an advertiser. As you may have probably heard, Amazon runs a very popular affiliate program that is called as Amazon Associates. You can earn commissions from each sale that results from the links and banners you placed on your website by using this program. Read more about Amazon Associates program and register for free at http://affiliate-program.amazon.com. As the example will be a fictional one here, you don't essentially need to register at Amazon affiliate program before starting. The example will help you understand how to add any advertiser in a similar way. Let's log in to OpenX Authentication panel. Use the Username and Password that we have created earlier.The login page looks like this: Click on Inventory tab at the top menu and then click on Add new advertiser link. We are now in Add new advertiser page. Fill Name, Contact, and Email fields. You can type your own information for Contact and Email fields. Leave other fields as they are, untouched with default settings. Click Save Changes button to complete adding an advertiser. What just happened We have learned how to add a new advertiser to OpenX. We have logged into OpenX management screen using the administrator user and provided the basic necessary fields: Name, Contact, and Email. Time for action – adding a campaign for Amazon.com Now, let's add a simple campaign for Amazon.com. Click on Add new campaign link near Amazon advertiser on Advertisers page. Fill the Name field in as Amazon – Toys & Games and select Contract (Exclusive) option under it Leave Date, Pricing, and Priority in relation to other campaign sections to their default settings. Leave Delivery capping per visitor and Miscellaneous sections untouched as well. Click on Save Changes button to complete adding Amazon - Toys & Games campaign. What just happened We have learned how to add a campaign for an advertiser using minimum requirements. We used Name and Campaign type fields and ignored other fields as we will cover them later.
Read more
  • 0
  • 0
  • 2333

article-image-start-ad-serving-openx-sequel
Packt
29 Mar 2010
2 min read
Save for later

Start Ad Serving with OpenX- A Sequel

Packt
29 Mar 2010
2 min read
Time for action – adding a zone to the website In this section, we will add a zone to the website. Click on Add new zone link on websites screen near our newly defined website name. Note that your website name differs from this sample. We only need to fill in the Name field and choose the Size that is exactly the size of the banner we have provided before. As our banner size is 728x90, we do the same for zone size. The fi elds highlighted with a red rectangular border in the following screenshot show these fields: Click Save Changes button to complete adding a zone for the website. What just happened We have learned how to add a zone for the newly added website definition. We defined a name for the zone and a size, which is the same size as the previously uploaded banner Time for action – linking the Amazon banner to the zone In this section, we will link the banner to the newly created zone Click on the Linked Banners link near our zone definition. Select Link individual banners option. Let's browse until we find our banner and let's choose it. Click the small arrow near the banner name. Now, we have completed linking the banner to our zone. What just happened We have learned how to link a banner to a website zone. We chose Link individual banners option, then browsed advertisers, campaign, and banners lists until we find and choose our banner from the list. Note that banner and zone sizes have to match in order to list the available banners in the linking screen.
Read more
  • 0
  • 0
  • 1467

article-image-openx-advanced-reports
Packt
29 Mar 2010
3 min read
Save for later

OpenX Advanced Reports

Packt
29 Mar 2010
3 min read
Advanced reports section provides a more organized and easier way to analyze OpenX statistics to work further with MS Excel. There are three types of advanced analysis reports: Advertising analysis report: This report provides a breakdown of advertising statistics for a particular advertiser or publisher. The generated report has three sections—daily, campaign, and zone breakdowns. Campaign analysis report: This report type is suitable to analyze the details of a particular campaign. There are three breakdowns including day, banner, and zone. Campaign delivery report: This advanced report shows the delivery statistics of campaigns for a certain selected period. It also highlights the underperforming campaigns. Advertising analysis report Now let's learn how to retrieve one of the most useful analysis reports in OpenX. Time for action – getting advertising analysis reports Click on Advanced Reports link in Statistics menu. Click on Advertising Analysis Report link in Standard Reports section. Click on the Generate button, without changing any options. Choose Open with Microsoft Office Excel (default) and click on OK button. We can see the Daily Breakdown tab selected by default. Click on Campaign Breakdown tab to get the details of all campaigns. Finally, click on Zone Breakdown tab to see the statistics of all zones What just happened? We learned how to generate an advertising analysis report in a few steps. This helps us get very useful statistics to determine the daily progress, success rates of campaigns, and zones that are organized in separate Excel tabs for easy analysis. Have a go hero – exploring other advanced report types Now, go and examine other report types including campaign analysis report and campaign delivery report. Try to understand the differences between the report types. Have a go hero – playing with statistics columns Try to specify the columns you need for online statistics screen using the account preferences. Pop quiz – understanding OpenX reports and statistics Suppose we (as an account manager) opened an advertiser account user for one of the advertisers. Which of the following report types will be available for the new user? Advertiser analysis report Campaign analysis report Campaign delivery report All of the reports None of the reports Summary In this article series, we have learned how to use reports and online statistics to evaluate the performance of OpenX advertisers, campaigns, banners, websites, and zones. We have specifically covered: Advertisers and campaigns statistics Exporting data to Excel for further analysis Types of advanced OpenX reports Getting advertising analysis reports in Excel If you have read this article you may be interested to view : Reports and Statistics in OpenX Ad Server Start Ad Serving with OpenX Start Ad Serving with OpenX- A Sequel
Read more
  • 0
  • 0
  • 1441

article-image-php-web-20-mashup-projects-your-own-video-jukebox-part-1
Packt
19 Feb 2010
11 min read
Save for later

PHP Web 2.0 Mashup Projects: Your Own Video Jukebox: Part 1

Packt
19 Feb 2010
11 min read
Project Overview What Mashup the web APIs from Last.fm and YouTube to create a video jukebox of songs Protocols Used REST (XML-RPC available) Data Formats XML, XPSF, RSS Tools Featured PEAR APIs Used Last.fm and YouTube   Now that we've had some experience using web services, it's time to fine tune their use. XML-RPC, REST, and SOAP will be frequent companions when you use web services and create mashups. You will encounter a lot of different data formats, and interesting ways in which the PHP community has dealt with these formats. This is especially true because REST has become so popular. In REST, with no formalized response format, you will encounter return formats that vary from plain text to ad-hoc XML to XML-based standards. The rest of our projects will focus on exposing us to some new formats, and we will look at how to handle them through PHP. We will begin with a project to create our own personalized video jukebox. This mashup will pull music lists feeds from the social music site, Last.fm. We will parse out artist names and song titles from these feeds and use that information to search videos on YouTube, a user-contributed video site, using the YouTube web service. By basing the song selections on ever-changing feeds, our jukebox selection will not be static, and will change as our music taste evolves. As YouTube is a user-contributed site, we will see many interesting interpretations of our music, too. This jukebox will be personalized, dynamic, and quite interesting. Both Last.fm and YouTube's APIs offer their web services through REST, and YouTube additionally offers an XML-RPC interface. Like with previous APIs, XML is returned with each service call. Last.fm returns either plain text, an XML playlist format called XSPF (XML Shareable Playlist Format), or RSS (Really Simple Syndication). In the case of YouTube, the service returns a proprietary format. Previously, we wrote our own SAX-based XML parser to extract XML data. In this article, we will take a look at how PEAR, the PHP Extension and Application Repository, can do the XSPF parsing work for us on this project and might help in other projects. Let's take a look at the various data formats we will be using, and then the web services themselves. XSPF One of XML's original goals was to allow industries to create their own markup languages to exchange data. Because anyone can create their own elements and schemas, as long as people agreed on a format, XML can be used as the universal data transmission language for that industry. One of the earliest XML-based languages was ChemXML, a language used to transmit data within the chemical industry. Since then, many others have popped up. XSPF was a complete grassroots project to create an open, non-proprietary music playlist format based on XML. Historically, playlists for software media players and music devices were designed to be used only on the machine or device, and schemas were designed by the vendor themselves. XSPF's goal was to create a format that could be used in software, devices, and across networks. XSPF is a very simple format, and is easy to understand. The project home page is at http://www.xspf.org. There, you will find a quick start guide which outlines a simple playlist as well as the official specifications at http://www.xspf.org/specs. Basically, a typical playlist has the following structure: <?xml version="1.0" encoding="UTF-8"?><playlist version="1" > <title>Shu Chow's Playlist</title> <date>2006-11-24T12:01:21Z</data> <trackList> <track> <title>Pure</title> <creator>Lightning Seeds</creator> <location> file:///Users/schow/Music/Pure.mp3 </location> </track> <track> <title>Roadrunner</title> <creator>The Modern Lovers</creator> <location> file:///Users/schow/Music/Roadrunner.mp3 </location> </track> <track> <title>The Bells</title> <creator>April Smith</creator> <location> file:///Users/schow/Music/The_Bells.mp3 </location> </track> </trackList></playlist> playlist is the parent element for the whole document. It requires one child element, trackList, but there can be several child elements that are the metadata for the playlist itself. In this example, the playlist has a title specified in the title element, and the creation date is specified in the date element. Underneath trackList are the individual tracks that make up the playlist. Each track is encapsulated by the track element. Information about the track, including the location of its file, is encapsulated in elements underneath track. In our example, each track has a title, an artist name, and a local file location. The official specifications allow for more track information elements such as track length and album information. Here are the playlist child elements summarized: Playlist Child Element Required? Description trackList Yes The parent of individual track elements. This is the only required child element of a playlist. Can be empty if the playlist has no songs. title No A human readable title of the XSPF playlist. creator No The name of the playlist creator. annotation No Comments on the playlist. info No A URL to a page containing more information about the playlist. location No The URL to the playlist itself. identifier No The unique ID for the playlist. Must be a legal Uniform Resource Name (URN). image No A URL to an image representing the playlist. date No The creation (not the last modified!) date of the playlist. Must be in XML schema dateTime format. For example, "2004-02-27T03:30:00". license No If the playlist is under a license, the license is specified with this element. attribution No If the playlist is modified from another source, the attribution element gives credit back to the original source, if necessary. link No Allows non-XSPF resources to be included in the playlist. meta No Allows non-XSPF metadata to be included in the playlist. extension No Allows non-XSPF XML extensions to be included in the playlist. A trackList element has an unlimited number of track elements to represent each track. track is the only allowed child of trackList. track's child elements give us information about each track. The following table summarizes the children of track: Track Child Element Required? Description location No The URL to the audio file of the track. identifier No The canonical ID for the playlist. Must be a legal URN. title No A human readable title of the track. Usually, the song's name. creator No The name of the track creator. Usually, the song's artist. annotation No Comments on the track. info No A URL to a page containing more information about the track. image No A URL to an image representing the track. album No The name of the album that the track belongs to. trackNum No The ordinal number position of the track in the album. duration No The time to play the track in milliseconds. link No Allows non-XSPF resources to be included in the track. meta No Allows non-XSPF metadata to be included in the track. extension No Allows non-XSPF XML extensions to be included in the track. Note that XSPF is very simple and track oriented. It was not designed to be a repository or database for songs. There are not a lot of options to manipulate the list. XSPF is merely a shareable playlist format, and nothing more. RSS The simplest answer to, "What is RSS?", is that it's an XML file used to publish frequently updated information, like news items, blogs entries, or links to podcast episodes. News sites like Slashdot.org and the New York Times provide their news items in RSS format. As new news items are published, they are added to the RSS feed. Being XML-based, third-party aggregator software makes reading news items easy. With one piece of software, I can tell it to grab feeds from various sources and read the news items in one location. Web applications can also read and parse RSS files. By offering an RSS feed for my blog, another site can grab the feed and keep track of my daily life. This is one way by which a small site can provide rudimentary web services with minimal investment. The more honest answer is that it is a group of XML standards (used to publish frequently updated information like news items or blogs) that may have little compatibility with each other. Each version release also has a tale of conflict and strife behind it. We won't dwell on the politicking of RSS. We'll just look at the outcomes. The RSS world now has three main flavors: The RSS 1.0 branch includes versions 0.90, 1.0, and 1.1. It's goal is to be extensible and flexible. The downside to the goals is that it is a complex standard. The RSS 2.0 branch includes versions 0.91, 0.92, and 2.0.x. Its goal is to be simple and easy to use. The drawback to this branch is that it may not be powerful enough for complex sites and feeds. There are some basic skeletal similarities between the two formats. After the XML root element, metadata about the feed itself is provided in a top section. After the metadata, one or more items follow. These items can be news stories, blog entries, or podcasts episodes. These items are the meat of an RSS feed. The following is an example RSS 1.1 file from XML.com: <Channel rdf_about="http://www.xml.com/xml/news.rss"> <title>XML.com</title> <link>http://xml.com/pub</link> <description> XML.com features a rich mix of information and services for the XML community. </description> <image rdf_parseType="Resource"> <title>XML.com</title> <url>http://xml.com/universal/images/xml_tiny.gif</url> </image> <items rdf_parseType="Collection"> <item rdf_about= "http://www.xml.com/pub/a/2005/01/05/restful.html"> <title> The Restful Web: Amazon's Simple Queue Service </title> <link> http://www.xml.com/pub/a/2005/01/05/restful.html </link> <description> In Joe Gregorio's latest Restful Web column, he explains that Amazon's Simple Queue Service, a web service offering a queue for reliable storage of transient messages, isn't as RESTful as it claims. </description> </item> <item rdf_about= "http://www.xml.com/pub/a/2005/01/05/tr-xml.html"> <title> Transforming XML: Extending XSLT with EXSLT </title> <link> http://www.xml.com/pub/a/2005/01/05/tr-xml.html </link> <description> In this month's Transforming XML column, Bob DuCharme reports happily that the promise of XSLT extensibility via EXSLT has become a reality. </description> </item> </items></Channel> The root element of an RSS file is an element named Channel. Immediately, after the root element are elements that describe the publisher and the feed. The title, link, description, and image elements give us more information about the feed. The actual content is nested in the items element. Even if there are no items in the feed, the items element is required, but will be empty. Usage of these elements can be summarized as follows: Channel Child Element Required? Description title Yes A human readable title of the channel. link Yes A URL to the feed. description Yes A human readable description of the feed. items Yes A parent element to wrap around item elements. image No A section to house information about an official image for the feed. others No Any other elements not in the RSS namespace can be optionally included here. The namespace must have been declared earlier, and the child elements must be prefixed. If used, the image element needs its own child elements to hold information about the feed image. A title element is required and while optional, a link element to the actual URL of the image would be extremely useful. Each news blog, or podcast entry is represented by an item element. In this RSS file, each item has a title, link, and a description, each, represented by the respective element. This file has two items in it before the items and Channel elements are closed off.
Read more
  • 0
  • 0
  • 1988

article-image-php-web-20-mashup-projects-your-own-video-jukebox-part-2
Packt
19 Feb 2010
19 min read
Save for later

PHP Web 2.0 Mashup Projects: Your Own Video Jukebox: Part 2

Packt
19 Feb 2010
19 min read
Parsing With PEAR If we were to start mashing up right now, between XSPF, YouTube's XML response, and RSS, we would have to create three different parsers to handle all three response formats. We would have to comb through the documentation and create flexible parsers for all three formats. If the XML response for any of these formats changes, we would also be responsible for changing our parser code. This isn't a difficult task, but we should be aware that someone else has already done the work for us. Someone else has already dissected the XML code. To save time, we can leverage this work for our mashup. We used PEAR, earlier in Chapter 1 to help with XML-RPC parsing. For this project, we will once again use PEAR to save us the trouble of writing parsers for the three XML formats we will encounter. For this project, we will take a look at three packages for our mashup. File_XSPF is a package for extracting and setting up XSPF playlists. Services_YouTube is a Web Services package that was created specifically for handling the YouTube API for us. Finally, XML_RSS is a package for working with RSS feeds. For this project, it works out well that there are three specific packages that fits our XML and RSS formats. If you need to work with an XML format that does not have a specific PEAR package, you can use the XML_Unserializer package. This package will take a XML and return it as a string. Is PEAR Right For You?Before we start installing PEAR packages, we should take a look if it is even feasible to use them for a project. PEAR packages are installed with a command line package manager that is included with every core installation of PHP. In order for you to install PEAR packages, you need to have administrative access to the server. If you are in a shared hosting environment and your hosting company is stingy, or if you are in a strict corporate environment where getting a server change is more hassle than it is worth, PEAR installation may not be allowed. You could get around this by downloading the PEAR files and installing them in your web documents directory. However, you will then have to manage package dependencies and package updates by yourself. This hassle may be more trouble than it's worth, and you may be better off writing your own code to handle the functionality.On the other hand, PEAR packages are often a great time saver. The purpose of the packages is to either simplify tedious tasks, or interface with complex systems. The PEAR developer has done the difficult work for you already. Moreover, as they are written in PHP and not C, like a PHP extension would be, a competent PHP developer should be able to read the code for documentation if it is lacking. Finally, one key benefit of many packages, including the ones we will be looking at, is that they are object-oriented representations of whatever they are interfacing. Values can be extracted by simply calling an object's properties, and complex connections can be ignited by a simple function call. This helps keep our code cleaner and modular. Whether the benefits of PEAR outweigh the potential obstacles depends on your specific situation. Package Installation and Usage Just like when we installed the XML-RPC package, we will use the install binary to install our three packages. If you recall, installing a package, simply type install into the command line followed by the name of the package. In this case, though, we need to set a few more flags to force the installer to grab dependencies and code in beta status. To install File_XSPF, switch to the root user of the machine and use this command: [Blossom:~] shuchow# /usr/local/php5/bin/pear install -f --alldeps File_XSPF This command will download the package. The -alldeps flag tells PEAR to also check for required dependencies and install them if necessary. The progress and outcome of the downloads will be reported. Do a similar command for Services_YouTube: [Blossom:~] shuchow# /usr/local/php5/bin/pear install -f --alldeps Services_YouTube Usually, you will not need the –f flag. By default, PEAR downloads the latest stable release of a package. The –f flag, force, forces PEAR to download the most current version, regardless of its release state. As of this writing, File_XSPF and Services_YouTube do not have stable releases, only beta and alpha respectively. Therefore, we must use –f to grab and install this package. Otherwise, PEAR will complain that the latest version is not available. If the package you want to download is in release state, you will not need the –f flag. This is the case of XML_RSS, which has a stable version available. [Blossom:~] shuchow# /usr/local/php5/bin/pear install --alldeps XML_RSS After this, sending a list-all command to PEAR will show the three new packages along with the packages you had before. PEAR packages are basically self-contained PHP files that PEAR installs into your PHP includes directory. The includes directory is a directive in your php.ini file. Navigate to this directory to see the PEAR packages' source files. To use a PEAR package, you will need to include the package's source file in the top of your code. Consult the package's documentation on how to include the main package file. For example, File_XSPF is activated by including a file named XSPF.php. PEAR places XSPF.php in a directory named File, and that directory is inside your includes directory. <?php require_once 'File/XSPF.php'; //File_XSPF is now available. File_XSPF The documentation to the latest version of XSPF is located at http://pear.php.net/package/File_XSPF/docs/latest/File_XSPF/File_XSPF.html. The package is simple to use. The heart of the package is an object called XSPF. You instantiate and use this object to interact with a playlist. It has methods to retrieve and modify values from a playlist, as well as utility methods to load a playlist into memory, write a playlist from memory to a file, and convert an XSPF file to other formats. Getting information from a playlist consists of two straightforward steps. First, the location of the XSPF file is passed to the XSPF object's parse method. This loads the file into memory. After the file is loaded, you can use the object's various getter methods to extract values from the list. Most of the XSPF getter methods are related to getting metadata about the playlist itself. To get information about the tracks in the playlist, use the getTracks method. This method will return an array of XSPF_Track objects. Each track in the playlist is represented as an XSPF_Track object in this array. You can then use the XSPF_Track object's methods to grab information about the individual tracks. We can grab a playlist from Last.fm to illustrate how this works. The web service has a playlist of a member's most played songs. Named Top Tracks, the playlist is located at http://ws.audioscrobbler.com/1.0/user/USERNAME/toptracks.xspf, where USERNAME is the name of the Last.fm user that you want to query. This page is named XSPFPEARTest.php in the examples. It uses File_XSPF to display my top tracks playlist from Last.fm. <?php require_once 'File/XSPF.php'; $xspfObj =& new File_XSPF(); //Load the playlist into the XSPF object. $xspfObj->parseFile('http://ws.audioscrobbler.com/1.0/user/ ShuTheMoody/toptracks.xspf'); //Get all tracks in the playlist. $tracks = $xspfObj->getTracks();?> This first section creates the XSPF object and loads the playlist. First, we bring in the File_XSPF package into the script. Then, we instantiate the object. The parseFile method is used to load an XSPF file list across a network. This ties the playlist to the XSPF object. We then use the getTracks method to transform the songs on the playlist into XSPF_Track objects. <html><head> <title>Shu Chow's Last.fm Top Tracks</title></head><body> Title: <?= $xspfObj->getTitle() ?><br /> Created By: <?= $xspfObj->getCreator() ?> Next, we prepare to display the playlist. Before we do that, we extract some information about the playlist. The XSPF object's getTitle method returns the XSPF file's title element. getCreator returns the creator element of the file. <?php foreach ($tracks as $track) { ?> <p> Title: <?= $track->getTitle() ?><br /> Artist: <?= $track->getCreator() ?><br /> </p><?php } ?></body></html> Finally, we loop through the tracks array. We assign the array's elements, which are XSPF_Track objects, into the $track variable. XSPF_Track also has getTitle and getCreator methods. Unlike XSPF's methods of the same names, getTitle returns the title of the track, and getCreator returns the track's artist. Running this file in your web browser will return a list populated with data from Last.fm. Services_YouTube Services_YouTube works in a manner very similar to File_XSPF. Like File_XSPF, it is an object-oriented abstraction layer on top of a more complicated system. In this case, the system is the YouTube API. Using Services_YouTube is a lot like using File_XSPF. Include the package in your code, instantiate a Services_YouTube object, and use this object's methods to interact with the service. The official documentation for the latest release of Services_YouTube is located at http://pear.php.net/package/Services_YouTube/docs/latest/. The package also contains online working examples at http://pear.php.net/manual/en/package.webservices.services-youtube.php. Many of the methods deal with getting members' information like their profile and videos they've uploaded. A smaller, but very important subset is used to query YouTube for videos. We will use this subset in our mashup. To get a list of videos that have been tagged with a specific tag, use the object's listByTag method. listByTag will query the YouTube service and store the XML response in memory. It is does not return an array of video objects we can directly manage, but with one additional function call, we can achieve this. From there, we can loop through an array of videos similar to what we did for XSPF tracks. The example file YouTubePearTest.php illustrates this process. <?php require_once 'Services/YouTube.php'; $dev_id = 'Your YouTube DeveloperID'; $tag = 'Social Distortion'; $youtube = new Services_YouTube($dev_id, array('usesCache' => true)); $videos = $youtube->listByTag($tag);?> First, we load the Services_YouTube file into our script. As YouTube's web service requires a Developer ID, we store that information into a local variable. After that, we place the tag we want to search for in another local variable named $tag. In this example, we are going to check out which videos YouTube has for the one of the greatest bands of all time, Social Distortion. Service_YouTube's constructor takes this Developer ID and uses it whenever it queries the YouTube web service. The constructor can take an array of options as a parameter. One of the options is to use a local cache of the queries. It is considered good practice to use a cache, as to not slam the YouTube server and run up your requests quota. Another option is to specify either REST or XML-RPC as the protocol via the driver key in the options array. By default, Services_YouTube uses REST. Unless you have a burning requirement to use XML-RPC, you can leave it as is. Once instantiated, you can call listByTag to get the response from YouTube. listByTag takes only one parameter—the tag of our desire. Services_YouTube now has the results from YouTube. We can begin the display of the results. <html><head> <title>Social Distortion Videos</title></head><body> <h1>YouTube Query Results for Social Distortion</h1> Next, we will loop through the videos. In order to get an array of video objects, we first need to parse the XML response. We do that using Services_YouTube's xpath method, which will use the powerful XPATH query language to go through the XML and convert it into PHP objects. We pass the XPATH query into the method, which will give us an array of useful objects. We will take a closer look at XPATH and XPATH queries later in another project. For now, trust that the query //video will return an array of video objects that we can examine. Within the loop, we display each video's title, a thumbnail image of the video, and a hyperlink to the video itself. <?php foreach ($videos->xpath('//video') as $i => $video) { ?><p> Title: <?= $video->title ?><br /> <img src='<?= $video->thumbnail_url ?>' alt='<?= $video->title ?>' /><br /> <a href='<?= $video->url ?>'>URL</a></p><?php } ?></body></html> Running this query in our web browser will give us a results page of videos that match the search term we submitted. XML_RSS Like the other PEAR extensions, XML_RSS changes something very complex, RSS, into something very simple and easy to use, PHP objects. The complete documentation for this package is at http://pear.php.net/package/XML_RSS/docs/XML_RSS. There is a small difference to the basic philosophy of XML_RSS compared to Services_YouTube and File_XSPF. The latter two packages take information from whatever we're interested in, and place them into PHP object properties. For example, File_XSPF takes track names into a Track object, and you use a getTitle() getter method to get the title of the track. In Services_YouTube, it's the same principle, but the properties are public, and so there are no getter methods. You access the video's properties directly in the video object. In XML_RSS, the values we're interested in are stored in associative arrays. The available methods in this package get the arrays, then you manipulate them directly. It's a small difference, but you should be aware of it in case you want to look at the code. It also means that you will have to check the documentation of the package to see which array keys are available to you. Let's take a look at how this works in an example. The file is named RSSPEARTest.php in the example code. One of Audioscrobbler's feeds gives us an RSS file of songs that a user recently played. The feed isn't always populated because after a few hours, songs that are played aren't considered recent. In other words, songs will eventually drop off the feed simply because they are too old. Therefore, it's best to use this feed on a heavy user of Last.fm. RJ is a good example to use. He seems to always be listening to something. We'll grab his feed from Audioscrobbler: <?php include ("XML/RSS.php"); $rss =& new XML_RSS("http://ws.audioscrobbler.com/1.0/user/RJ/ recenttracks.rss"); $rss->parse(); We start off by including the module and creating an XML_RSS object. XML_RSS is where all of the array get methods reside, and is the heart of this package. It's constructor method takes one variable—the path to the RSS file. At instantiation, the package loads the RSS file into memory. parse() is the method that actually does the RSS parsing. After this, the get methods will return data about the feed. Needless to say, parse() must be called before you do anything constructive with the file. $channelInfo = $rss->getChannelInfo();?> The package's getChannelInfo() method returns an array that holds information about the metadata, the channel, of the file. This array holds the title, description, and link elements of the RSS file. Each of these elements is stored in the array with the same key name as the element. <?= "<?xml version="1.0" encoding="UTF-8" ?>" ?> The data that comes back will be UTF-8 encoded. Therefore, we need to force the page into UTF-8 encoding mode. This line outputs the XML declaration into the top of the web page in order to insure proper rendering. Putting a regular <?xml declaration will trigger the PHP engine to parse the declaration. However, PHP will not recognize the code and halt the page with an error. <html> <head> <title><?= $channelInfo['title'] ?></title> </head> <body> <h1><?= $channelInfo['description'] ?></h1> Here we begin the actual output of the page. We start by using the array returned from getChannelInfo() to output the title and description elements of the feed. <ol> <?php foreach ($rss->getItems() as $item { ?> <li> <?= $item['title'] ?>: <a href="<?= $item ['link'] ?>"><?= $item ['link'] ?></a> </li> <?php } ?></ol> Next, we start outputting the items in the RSS file. We use getItems() to grab information about the items in the RSS. The return is an array that we loop through with a foreach statement. Here, we are extracting the item's title and link elements. We show the title, and then create a hyperlink to the song's page on Last.fm. The description and pubDate elements in the RSS are also available to us in getItems's returned array. Link to User: <a href="<?= $channelInfo['link'] ?>"><?= $channelInfo['link'] ?></a> </body></html> Finally, we use the channel's link property to create a hyperlink to the user's Last.fm page before we close off the page's body and html tags. Using More ElementsIn this example, the available elements in the channel and item arrays are a bit limited. getChannelInfo() returns an array that only has the title, description, and link properties. The array from getItems() only has title, description, link, and pubDate properties. This is because we are using the latest release version of XML_RSS. At the time of writing this book, it is version 0.9.2. The later versions of XML_RSS, currently in beta, handle many more elements. Elements in RSS 2.0 like category and authors are available. To upgrade to a beta version of XML_RSS, use the command PEAR upgrade –f XML_RSS in the command line. The –f flag is the same flag we used to force the beta and alpha installations of Service_YouTube and File_XSPF. Alternatively, you can install the beta version of XML_RSS at the beginning using the same –f flag. If we run this page on our web browser, we can see the successful results of our hit. At this point, we know how to use the Audioscrobbler feeds to get information. The majority of the feeds are either XSPF or RSS format. We know generally how the YouTube API works. Most importantly, we know how to use the respective PEAR packages to extract information from each web service. It's time to start coding our application. Mashing Up If you haven't already, you should, at the very least, create a YouTube account and sign up for a developer key. You should also create a Last.fm account, install the client software, and start listening to some music on your computer. This will personalize the video jukebox to your music tastes. All examples here will assume that you are using your own YouTube key. I will use my own Last.fm account for the examples. As the feeds are open and free, you can use the same feeds if you choose not to create a Last.fm account. Mashup Architecture There are obviously many ways in which we can set up our application. However, we're going to keep functionality fairly simple. The interface will be a framed web page. The top pane is the navigation pane. It will be for the song selection. The bottom section is the content pane and will display and play the video. In the navigation pane, we will create a select menu with all of our songs. The value, and label, for each option will be the artist name followed by a dash, followed by the name of the song (For example, "April Smith—Bright White Jackets"). Providing both pieces of information will help YouTube narrow down the selection. When the user selects a song and pushes a "Go" button, the application will load the content page into the content pane. This form will pass the artist and song information to the content page via a GET parameter. The content page will use this GET parameter to query YouTube. The page will pull up the first, most relevant result from its list of videos and display it. Main Page The main page is named jukebox.html in the example code. This is our frameset page. It will be quite simple. All it will do is define the frameset that we will use. <html><head><title>My Video Jukebox</title></head> <frameset rows="10%,90%"> <frame src="navigation.php" name="Navigation" /> <frame src="" name="Content" /> </frameset></html> This code defines our page. It is two frame rows. The navigation section, named Navigation, is 10% of the height, and the content, named Content, is the remaining 90%. When first loaded, the mashup will load the list of songs in the navigation page and nothing else.
Read more
  • 0
  • 0
  • 1805

article-image-simple-item-selector-using-jquery
Packt
09 Nov 2009
4 min read
Save for later

Simple Item Selector Using jQuery

Packt
09 Nov 2009
4 min read
(For more resources on jQuery, see here.) Adding jQuery to your page You can download the latest version of jQuery from jQuery site (http://jquery.com/) and can be added as a reference to your web pages accordingly. You can reference a local copy of jQuery using <script> tag in the page. Either you can reference your local copy or you can directly reference remote copy from jQuery.com or Google Ajax API (http://ajax.googleapis.com/ajax/libs/jquery/1.3/jquery.min.js) Prerequisite Knowledge In order to understand the code, one should have the basic knowledge of HTML, CSS, JavaScript and basic knowledge of jQuery. Ingredients Used HTML CSS jQuery Photoshop (Used for Designing of Image Buttons and Backgrounds) Preview / Download If you would like to see the working example, please do click here http://www.developersnippets.com/snippets/jquery/item_selector/item_selector.html). And if you would like to download the snippet, click here (http://www.developersnippets.com/snippets/jquery/item_selector/item_selector.zip) Figure 1: Snapshot of "Simple Item Selector using jQuery" Figure 2: Overview of div containers and image buttons used Successfully tested The above application has been successfully tested on various browsers like IE 6.0, IE 7, IE 8, Mozilla Firefox (Latest Version), Google Chrome and Safari Browser (4.0.2) respectively. HTML Code Below is the HTML code with comments for you to understand it better. <!-- Container --><div id="container"> <!-- From Container --> <div class="from_container"> <select id="fromSelectBox" multiple="multiple"> <option value="1">Adobe</option> <option value="2">Oracle</option> <option value="3">Google</option> <option value="4">Microsoft</option> <option value="5">Google Talk</option> <option value="6">Google Wave</option> <option value="7">Microsoft Silver Light</option> <option value="8">Adobe Flex Professional</option> <option value="9">Oracle DataBase</option> <option value="10">Microsoft Bing</option> </select><br /> <input type="image" src="images/selectall.jpg" class="selectall" onclick="selectAll('fromSelectBox')" /><input type="image" src="images/deselectall.jpg" class="deselectall" onclick="clearAll('fromSelectBox')" /> </div> <!-- From Container [Close] --> <!-- Buttons Container --> <div class="buttons_container"> <input type="image" src="images/topmost.jpg" id="topmost" /><br /> <input type="image" src="images/moveup.jpg" id="moveup" /><br /> <input type="image" src="images/moveright.jpg" id="moveright" /><br /> <input type="image" src="images/moveleft.jpg" id="moveleft" /><br /> <input type="image" src="images/movedown.jpg" id="movedown" /><br /> <input type="image" src="images/bottommost.jpg" id="bottommost" /><br /> </div> <!-- Buttons Container [Close] --> <!-- To Container --> <div class="to_container"> <select id="toSelectBox" multiple="multiple"></select><br /> <input type="image" src="images/selectall.jpg" class="selectall" onclick="selectAll('toSelectBox')" /><input type="image" src="images/deselectall.jpg" class="deselectall" onclick="clearAll('toSelectBox')" /> </div> <!-- To Container [Close] --> <!-- To Container --> <div class="ascdes_container"> <input type="image" src="images/ascending.jpg" id="ascendingorder" style="margin:1px 0px 2px 0px;" onclick="ascOrderFunction()" /><br /> <input type="image" src="images/descending.jpg" id="descendingorder" onclick="desOrderFunction()" /> </div> <!-- To Container [Close] --> <div style="clear:both"></div></div><!-- Container [Close] -->
Read more
  • 0
  • 0
  • 2525
Unlock access to the largest independent learning library in Tech for FREE!
Get unlimited access to 7500+ expert-authored eBooks and video courses covering every tech area you can think of.
Renews at $19.99/month. Cancel anytime
article-image-how-bridge-client-server-gap-using-ajax-part-i
Packt
28 Oct 2009
18 min read
Save for later

How to Bridge the Client-Server Gap using AJAX (Part I)

Packt
28 Oct 2009
18 min read
Technically, AJAX is an acronym standing for Asynchronous JavaScript and XML. The technologies involved in an AJAX solution include: JavaScript, to capture interactions with the user or other browser-related events The XMLHttpRequest object, which allows requests to be made to the server without interrupting other browser tasks XML files on the server, or often other similar data formats such as HTML or JSON More JavaScript, to interpret the data from the server and present it on the page Many frameworks have sprung up to assist developers in taming it, because of the inconsistencies in the browsers' implementations of the XMLHttpRequest object; jQuery is no exception. Let us see if AJAX can truly perform miracles. Loading data on demand Underneath all the hype and trappings, AJAX is just a means of loading data from the server to the web browser, or client, without a visible page refresh. This data can take many forms, and we have many options for what to do with it when it arrives. We'll see this by performing the same basic task in many ways. We are going to build a page that displays entries from a dictionary, grouped by the starting letter of the dictionary entry. The HTML defining the content area of the page will look like this: <div id="dictionary"></div> Yes, really! Our page will have no content to begin with. We are going to use jQuery's various AJAX methods to populate this <div> with dictionary entries. <div class="letters"> <div class="letter" id="letter-a"> <h3><a href="#">A</a></h3> </div> <div class="letter" id="letter-b"> <h3><a href="#">B</a></h3> </div> <div class="letter" id="letter-c"> <h3><a href="#">C</a></h3> </div> <div class="letter" id="letter-d"> <h3><a href="#">D</a></h3> </div></div> As always, a real-world implementation should use progressive enhancement to make the page function without requiring JavaScript. Here, to simplify our example, the links do nothing until we add behaviors to them with jQuery. As always, a real-world implementation should use progressive enhancement to make the page function without requiring JavaScript. Here, to simplify our example, the links do nothing until we add behaviors to them with jQuery. Adding a few CSS rules, we get a page that looks like this: Now we can focus on getting content onto the page. Appending HTML AJAX applications are often no more than a request for a chunk of HTML. This technique, sometimes referred to as AHAH (Asynchronous HTTP and HTML), is almost trivial to implement with jQuery. First we need some HTML to insert, which we'll place in a file called a.html alongside our main document. This secondary HTML file begins: <div class="entry"> <h3 class="term">ABDICATION</h3> <div class="part">n.</div> <div class="definition"> An act whereby a sovereign attests his sense of the high temperature of the throne. <div class="quote"> <div class="quote-line">Poor Isabella's Dead, whose abdication</div> <div class="quote-line">Set all tongues wagging in the Spanish nation.</div> <div class="quote-line">For that performance 'twere unfair to scold her:</div> <div class="quote-line">She wisely left a throne too hot to hold her.</div> <div class="quote-line">To History she'll be no royal riddle &mdash;</div> <div class="quote-line">Merely a plain parched pea that jumped the griddle.</div> <div class="quote-author">G.J.</div> </div> </div></div><div class="entry"> <h3 class="term">ABSOLUTE</h3> <div class="part">adj.</div> <div class="definition"> Independent, irresponsible. An absolute monarchy is one in which the sovereign does as he pleases so long as he pleases the assassins. Not many absolute monarchies are left, most of them having been replaced by limited monarchies, where the sovereign's power for evil (and for good) is greatly curtailed, and by republics, which are governed by chance. </div></div> The page continues with more entries in this HTML structure. Rendered on its own, this page is quite plain:   Note that a.html is not a true HTML document; it contains no <html>, <head>, or <body>, all of which are normally required. We usually call such a file a snippet or fragment; its only purpose is to be inserted into another HTML document, which we'll accomplish now: $(document).ready(function() { $('#letter-a a').click(function() { $('#dictionary').load('a.html'); return false; });}); The .load() method does all our heavy lifting for us! We specify the target location for the HTML snippet by using a normal jQuery selector, and then pass the URL of the file to be loaded as a parameter to the method. Now, when the first link is clicked, the file is loaded and placed inside <div id="dictionary">. The browser will render the new HTML as soon as it is inserted:   Note that the HTML is now styled, whereas before it was plain. This is due to the CSS rules in the main document; as soon as the new HTML snippet is inserted, the rules apply to its tags as well. When testing this example, the dictionary definitions will probably appear instantaneously when the button is clicked. This is a hazard of working on our applications locally; it is hard to account for delays in transferring documents across the network. Suppose we added an alert box to display after the definitions are loaded: $(document).ready(function() { $('#letter-a a').click(function() { $('#dictionary').load('a.html'); alert('Loaded!'); return false; });}); However, when this particular code is tested on a production web server, the alert will quite possibly have come and gone before the load has completed, due to network lag. This happens because all AJAX calls are by default asynchronous. Otherwise, we'd have to call it SJAX, which hardly has the same ring to it! Asynchronous loading means that once the HTTP request to retrieve the HTML snippet is issued, script execution immediately resumes without waiting. Sometime later, the browser receives the response from the server and handles it. This is generally desired behavior; it is unfriendly to lock up the whole web browser while waiting for data to be retrieved. If actions must be delayed until the load has been completed, jQuery provides a callback for this. An example will be provided below.   Working with JavaScript objects Pulling in fully-formed HTML on demand is very convenient, but there are times when we want our script to be able to do some processing of the data before it is displayed. In this case, we need to retrieve the data in a structure that we can traverse with JavaScript. With jQuery's selectors, we could traverse the HTML we get back and manipulate it, but it must first be inserted into the document. A more native JavaScript data format can mean even less code. Retrieving a JavaScript object As we have often seen, JavaScript objects are just sets of key-value pairs, and can be defined succinctly using curly braces ({}). JavaScript arrays, on the other hand, are defined on the fly with square brackets ([]). Combining these two concepts, we can easily express some very complex and rich data structures. The term JavaScript Object Notation (JSON) was coined by Douglas Crockford to capitalize on this simple syntax. This notation can offer a concise alternative to the sometimes-bulky XML format: { "key": "value", "key 2": [ "array", "of", "items" ]} For information on some of the potential advantages of JSON, as well as implementations in many programming languages, visit http://json.org/ . We can encode our data using this format in many ways. We'll place some dictionary entries in a JSON file we'll call b.json, which begins as follows: [ { "term": "BACCHUS", "part": "n.", "definition": "A convenient deity invented by the...", "quote": [ "Is public worship, then, a sin,", "That for devotions paid to Bacchus", "The lictors dare to run us in,", "And resolutely thump and whack us?" ], "author": "Jorace" }, { "term": "BACKBITE", "part": "v.t.", "definition": "To speak of a man as you find him when..." }, { "term": "BEARD", "part": "n.", "definition": "The hair that is commonly cut off by..." }, To retrieve this data, we'll use the $.getJSON() method, which fetches the file and processes it, providing the calling code with the resulting JavaScript object. Global jQuery functions To this point, all jQuery methods that we've used have been attached to a jQuery object that we've built with the $() factory function. The selectors have allowed us to specify a set of DOM nodes to work with, and the methods have operated on them in some way. This $.getJSON() function, however, is different. There is no logical DOM element to which it could apply; the resulting object has to be provided to the script, not injected into the page. For this reason, getJSON() is defined as a method of the global jQuery object (a single object called jQuery or $ defined once by the jQuery library), rather than of an individual jQuery object instance (the objects we create with the $() function). If JavaScript had classes like other object-oriented languages, we'd call $.getJSON() a class method. For our purposes, we'll refer to this type of method as a global function; in effect, they are functions that use the jQuery namespace so as not to conflict with other function names. To use this function, we pass it the file name as before: $(document).ready(function() { $('#letter-b a').click(function() { $.getJSON('b.json'); return false; });}); This code has no apparent effect when we click the link. The function call loads the file, but we have not told JavaScript what to do with the resulting data. For this, we need to use a callback function. The $.getJSON() function takes a second argument, which is a function to be called when the load is complete. As mentioned before, AJAX calls are asynchronous, and the callback provides a way to wait for the data to be transmitted rather than executing code right away. The callback function also takes an argument, which is filled with the resulting data. So, we can write: $(document).ready(function() { $('#letter-b a').click(function() { $.getJSON('b.json', function(data) { }); return false; });}); Here we are using an anonymous function as our callback, as has been common in our jQuery code for brevity. A named function could equally be provided as the callback. Inside this function, we can use the data variable to traverse the data structure as necessary. We'll need to iterate over the top-level array, building the HTML for each item. We could do this with a standard for loop, but instead we'll introduce another of jQuery's useful global functions, $.each(). Instead of operating on a jQuery object, this function takes an array or map as its first parameter and a callback function as its second. Each time through the loop, the current iteration index and the current item in the array or map are passed as two parameters to the callback function. $(document).ready(function() { $('#letter-b a').click(function() { $.getJSON('b.json', function(data) { $('#dictionary').empty(); $.each(data, function(entryIndex, entry) { var html = '<div class="entry">'; html += '<h3 class="term">' + entry['term'] + '</h3>'; html += '<div class="part">' + entry['part'] + '</div>'; html += '<div class="definition">'; html += entry['definition']; html += '</div>'; html += '</div>'; $('#dictionary').append(html); }); }); return false; });}); Before the loop, we empty out <div id="dictionary"> so that we can fill it with our newly-constructed HTML. Then we use $.each() to examine each item in turn, building an HTML structure using the contents of the entry map. Finally, we turn this HTML into a DOM tree by appending it to the <div>. This approach presumes that the data is safe for HTML consumption; it should not contain any stray < characters, for example. All that's left is to handle the entries with quotations, which takes another $.each() loop: $(document).ready(function() { $('#letter-b a').click(function() { $.getJSON('b.json', function(data) { $('#dictionary').empty(); $.each(data, function(entryIndex, entry) { var html = '<div class="entry">'; html += '<h3 class="term">' + entry['term'] + '</h3>'; html += '<div class="part">' + entry['part'] + '</div>'; html += '<div class="definition">'; html += entry['definition']; if (entry['quote']) { html += '<div class="quote">'; $.each(entry['quote'], function(lineIndex, line) { html += '<div class="quote-line">' + line + '</div>'; }); if (entry['author']) { html += '<div class="quote-author">' + entry['author'] + '</div>'; } html += '</div>'; } html += '</div>'; html += '</div>'; $('#dictionary').append(html); }); }); return false; });}); With this code in place, we can click the B link and confirm our results:   The JSON format is concise, but not forgiving. Every bracket, brace, quote, and comma must be present and accounted for, or the file will not load. In most browsers, we won't even get an error message; the script will just silently fail. Executing a script Occasionally we don't want to retrieve all the JavaScript we will need when the page is first loaded. We might not know what scripts will be necessary until some user interaction occurs. We could introduce <script> tags on the fly when they are needed, but a more elegant way to inject additional code is to have jQuery load the .js file directly. Pulling in a script is about as simple as loading an HTML fragment. In this case, we use the global function $.getScript(), which, like its siblings, accepts a URL locating the script file: $(document).ready(function() { $('#letter-c a').click(function() { $.getScript('c.js'); return false; });}); Scripts fetched in this way are run in the global context of the current page. This means they have access to all globally-defined functions and variables, notably including jQuery itself. We can therefore mimic the JSON example to prepare and insert HTML on the page when the script is executed, and place this code in c.js:. var entries = [ { "term": "CALAMITY", "part": "n.", "definition": "A more than commonly plain and..." }, { "term": "CANNIBAL", "part": "n.", "definition": "A gastronome of the old school who..." }, { "term": "CHILDHOOD", "part": "n.", "definition": "The period of human life intermediate..." }, { "term": "CLARIONET", "part": "n.", "definition": "An instrument of torture operated by..." }, { "term": "COMFORT", "part": "n.", "definition": "A state of mind produced by..." }, { "term": "CORSAIR", "part": "n.", "definition": "A politician of the seas." }];var html = '';$.each(entries, function() { html += '<div class="entry">'; html += '<h3 class="term">' + this['term'] + '</h3>'; html += '<div class="part">' + this['part'] + '</div>'; html += '<div class="definition">' + this['definition'] + '</div>'; html += '</div>';});$('#dictionary').html(html); Now clicking on the C link has the expected result: Loading an XML document XML is part of the acronym AJAX, but we haven't actually loaded any XML yet. Doing so is straightforward, and mirrors the JSON technique fairly closely. First we'll need an XML file d.xml containing some data we wish to display, excerpted here: <?xml version="1.0" encoding="UTF-8"?><entries> <entry term="DEFAME" part="v.t."> <definition> To lie about another. To tell the truth about another. </definition> </entry> <entry term="DEFENCELESS" part="adj."> <definition> Unable to attack. </definition> </entry> <entry term="DELUSION" part="n."> <definition> The father of a most respectable family, comprising Enthusiasm, Affection, Self-denial, Faith, Hope, Charity and many other goodly sons and daughters. </definition> <quote author="Mumfrey Mappel"> <line>All hail, Delusion! Were it not for thee</line> <line>The world turned topsy-turvy we should see; </line> <line>For Vice, respectable with cleanly fancies, </line> <line>Would fly abandoned Virtue's gross advances. </line> </quote> </entry> <entry term="DIE" part="n."> <definition> The singular of "dice." We seldom hear the word, because there is a prohibitory proverb, "Never say die." At long intervals, however, some one says: "The die is cast," which is not true, for it is cut. The word is found in an immortal couplet by that eminent poet and domestic economist, Senator Depew: </definition> <quote> <line>A cube of cheese no larger than a die</line> <line>May bait the trap to catch a nibbling mie.</line> </quote> </entry></entries> This data could be expressed in many ways, of course, and some would more closely mimic the structure we established for the HTML or JSON used earlier. Here, however, we're illustrating some of the features of XML designed to make it more readable to humans, such as the use of attributes for term and part rather than tags. $(document).ready(function() { $('#letter-d a').click(function() { $.get('d.xml', function(data) { }); return false; });}); This time it's the $.get() function that does our work. In general, this function simply fetches the file at the supplied URL and provides the plain text to the callback. However, if the response is known to be XML because of its server-supplied MIME type, the callback will be handed the XML DOM tree. Fortunately, as we have already seen, jQuery has substantial DOM traversing capabilities. We can use the normal .find(), .filter() and other traversal methods on the XML document just as we would on HTML: $(document).ready(function() { $('#letter-d a').click(function() { $.get('d.xml', function(data) { $('#dictionary').empty(); $(data).find('entry').each(function() { var $entry = $(this); var html = '<div class="entry">'; html += '<h3 class="term">' + $entry.attr('term') + '</h3>'; html += '<div class="part">' + $entry.attr('part') + '</div>'; html += '<div class="definition">'; html += $entry.find('definition').text(); var $quote = $entry.find('quote'); if ($quote.length) { html += '<div class="quote">'; $quote.find('line').each(function() { html += '<div class="quote-line">' + $(this).text() + '</div>'; }); if ($quote.attr('author')) { html += '<div class="quote-author">' + $quote.attr('author') + '</div>'; } html += '</div>'; } html += '</div>'; html += '</div>'; $('#dictionary').append($(html)); }); }); return false; });}); This has the expected effect when the D link is clicked: This is a new use for the DOM traversal methods we already know, shedding some light on the flexibility of jQuery's CSS selector support. CSS syntax is typically used to help beautify HTML pages, and thus selectors in standard .css files use HTML tag names such as div and body to locate content. However, jQuery can use arbitrary XML tag names, such as entry and definition here, just as readily as the standard HTML ones. The advanced selector engine inside jQuery facilitates finding parts of the XML document in much more complicated situations, as well. For example, suppose we wanted to limit the displayed entries to those that have quotes that in turn have attributed authors. To do this, we can limit the entries to those with nested <quote> elements by changing entry to entry:has(quote). Then we can further restrict the entries to those with author attributes on the <quote> elements by writing entry:has(quote[author]). The line with the initial selector now reads: $(data).find('entry:has(quote[author])').each(function() { This new selector expression restricts the returned entries correspondingly:    
Read more
  • 0
  • 0
  • 2265

article-image-real-content-php5-cms-part-1
Packt
27 Oct 2009
14 min read
Save for later

Real Content in PHP5 CMS: Part 1

Packt
27 Oct 2009
14 min read
The problem There are some common features in providing website content, but also many differences. Applications easily become complex as they tackle real world problems, and there has been much real innovation in web systems. So the areas to look at in this article are: Major areas for content development A review of minor yet important areas How a simple text manager is built An outline of a complex content delivery extension Discussion and considerations Now, we will work through the major areas of website content, devoting a section to each one. A round up of some less important aspects of content completes the discussion, leaving us ready to move on to details of implementation. Articles, blogs, magazines, and FAQ The most basic requirement is for text and pictures, and the simplest scheme needs little more than the standard database and a WYSIWYG editor. An extension that works at this level is illustrated later in the article. It is pretty much essential to have an ability to create items of this kind in an unpublished state so that they can be revised until ready for use. The state is then changed to published. Almost immediately, a further requirement arises to specify a range of publication dates, so that material aimed at a specific event can be automatically published at the appropriate time. Likewise, it is desirable to have an automatic mechanism for removing information that is no longer current, for example because it refers to a coming event in terms that will be irrelevant once the event has passed. A website that carries plainly obsolete articles is unlikely to be popular! There are many ways to organize textual material. One is to place it into some kind of tree structure, rather akin to the classification schemes used in libraries. Ideally, such a scheme has no particular constraints on the depth of the tree structure. A concern with this approach is that it can quickly lead to a conflict between two alternative uses—classification according to subject and classification according to reader permissions. An option that can be used in conjunction with a tree structure is to use some form of tagging. This introduces much greater flexibility in some respects, as it is easy to apply multiple tags to a single item of content, which can therefore be classified in a wide variety of ways, and can appear under multiple headings. A blog is an example of a system that might work best with a combination of a classification tree and a tagging scheme. Where there are several people creating blogs, the different authors fit well with a tree structure, since there is no question of an item belonging to more than one author. On the other hand, items are often tagged according to their subject matter, and several tags may be applicable to an individual article. If authors create more than one blog and there are questions about which visitors are able to see which blog, then careful thought needs to be given as to whether the split of blogs is best handled by the classification tree or by tagging. Using a tree achieves rigid separation, and is easily amenable to imposing access controls. But if the same item appears in more than one blog, then tagging works better as the item is ideally stored only once but has multiple tags. Blogs also frequently provide for comments, discussed in the next section. A magazine is typically a collection of articles. For a simple case, it might be adequate for the articles of the magazine to be equated to website pages, but a more sophisticated magazine would want to avoid restrictions of that kind. The basic unit of content would still need to be an individual article, but website pages then require some kind of template to build a page from multiple items. One popular application for quite simple content is the compilation of frequently asked questions (FAQ's). Advanced implementations might be described more grandly as knowledge bases. Again, both a classification tree and tagging can be relevant, but a useful FAQ (and especially one that wants to be a knowledge base) also needs effective search facilities so that information can be easily found. In all of these cases, added complexity arises if facilities like versioning are needed. Another similar issue is the need for workflow and differing roles, such as authors and editors. Mention of roles suggests a RBAC mechanism. It seems unlikely that one single model will ever meet every requirement in areas such as versioning and workflow. Version control can become extremely complex, and usually requires the allocation of roles that involve access rights and functional capabilities. Workflow is much the same. In both cases, though, simple and rigid schemes are liable to create problems. For example, the same person is quite likely to be an author in some situations, and an editor or publisher in others. A flexible and an efficient RBAC system is a pre-requisite for handling these problems, but as discussed earlier, the technical provision of RBAC is only a start. Applying it to particular systems and creating an appropriate user interface is a considerable challenge. Comments and reviews One of the successful innovations brought about by widespread use of the Web has been feedback through comments and reviews. Amazon is only one of many sites that now include reviews by customers of the products on sale. It could be said that this is a form of social networking, as the more sophisticated sites maintain profiles of reviewers and encourage them to achieve their own identity. Regular readers in particular areas of interest can get to know reviewers and form an opinion on the reliability of their views. There are two main problems with implementing comments and reviews. One is the question of how to generalize the facility, so as to avoid implementing it repeatedly in different applications. The other is how to deal with the ever present threat of spam. From the point of view of a developer, handling comments raises much the same issues regardless of what may be the subject of the comments. So blogs, selections of products, image galleries, and so on are all capable of having comments added to their items using similar mechanisms. This suggests a structure something like the scheme where the coarse grained structure is the component, but its display is achieved through the use of a template and a number of modules. Comments can thus be generated by a module that knows relatively little about the application, only enough to keep its comments separate from those for other applications and to relate a set of comments to a particular item, whether it is a blog item, product, gallery image, or whatever. That deals with the display of existing comments, which still leaves a requirement for a general interface that allows new comments to be added. The comment facility can easily enough handle the acceptance of a new comment, although it may need help if the page that accepts comments is to also show the object to which the comment applies. The comment facility also needs to know where to hand control once a new comment has been completed. Some moderately tricky detailed design is involved in providing an implementation of the full scheme. The other big problem with any facility that permits visitors to a site to enter information for display is that it attracts spammers. Usually, they arrive not in person but in the form of automated bots that can become very sophisticated. There are bots that know how to obtain an account, and log in to a range of systems. There are even bots that can handle CAPTCHAs (those messed up images out of which you are supposed to decipher letters or numbers). Some of the bots can handle CAPTCHAs better than some humans, which makes for accessibility problems. Fortunately, much link spamming is for the purpose of promoting websites, and so the spammer has to give away some information in the form of the link to the site being promoted. A reasonably effective defense against this kind of spamming is a collaborative scheme for blacklisting sites. Even that is not totally effective, as spammers find ways to create new sites quickly and cheaply, so that the threat is constantly changing. As with most forms of attack, there is unlikely to be any conclusion to this battle. Forums Forums are a very popular Web feature, providing a structured means for public or private discussion. Developing a forum is a major undertaking, and most people will prefer to choose from existing software products. Forum software usually provides for visitors to contribute messages, either starting a new topic or replying to an existing one. There is often a hierarchical structure to the messages so that a number of different areas of interest can be covered in a convenient way. Advanced systems include sophisticated user management, including support for a variety of different groups, which provides a means to decide who has access to which topics. Unwanted messages are a constant threat, and most active forums need moderators to weed them out. Development of a new forum will clearly need a number of the framework features discussed earlier. Robust user control is essential, and if different users are granted different access rights, a good system of RBAC is a requirement. A forum is highly amenable to the use of cache, since pages are likely to be constructed out of a number of database records, but the records are updated relatively infrequently. To be responsive, the cache needs to have a degree of intelligence so that pages with new contributions are refreshed quickly. Mail services are likely to be employed so that subscribers can receive notification of new contributions to topics in which they have registered an interest. Another approach is to seek a degree of integration between off the shelf forum software and the CMS. The most popular area for integration is user login. Obviously it is necessary to obtain some information about the way in which the forum software is implemented. Provided that can be found, then it is a relatively simple matter to integrate with a CMS that has been built with plentiful plug in triggers around the area of user authentication. From the point of view of visual integration, the amount of screen space needed by a forum is such that it is often difficult to build it within the framework of a typical CMS. Often a better approach is to build a custom theme for the forum that includes links back to the main site, so as to avoid completely losing continuity of navigation. Galleries, repositories, and streaming Although they have come from different requirements, galleries, and file repositories have a lot in common. Both start out simple and rapidly become complex. The general idea of a gallery is to build a collection of images, typically organized into categories and accessible via small versions of the images (thumbnails). File repositories have long been popular since the days of bulletin boards, where collections of files (often programs) were made available for download. Ideally the organization into categories (or folders or containers) is flexible with no particular limit on the depth to which subcategories can go. Some basic requirements relate to security. It is obviously essential to avoid hosting files that could contain malicious PHP code. This includes avoiding uploads of image files that contain PHP code embedded within actual image data. Simple checks can be fooled by this technique, but a block on the .php extension prevents the code being interpreted. Another potentially major security issue is bandwidth theft. If files or images are too easily accessed, then other sites may choose to use them without acknowledgment, transferring the bandwidth costs to the site hosting the material. As applications broaden, access control becomes an issue. Files are to be made available only to a restricted group, and uploads may be restricted more tightly again. There may be administrator oversight, with uploads needing approval. Once again, we are seeing a demand for an effective access control system, preferably role-based. In fact demands on systems of this kind can easily become very sophisticated, such as allowing users to have personal upload areas over which they have complete control to determine who is able to gain access. An RBAC system that is technically capable of handling this can be built relatively easily, although creating a good user interface is a challenge. Whether the system is a gallery or file repository, the use of thumbnail images is increasingly prevalent. File uploads may, therefore, be accompanied by one or more image files that are used to enhance the display of the files available. Information about the system is likely to be needed, such as which are the most recent additions to the collection, which items are most popular, who has accessed what, and who has uploaded what. Information of this kind can also contribute to security by providing an audit trail of what has been happening to the system. Streaming of files is a demand now often placed on a file repository, as the files can be audio or video files made available for immediate access. Streaming is simply a mode of file processing whereby the information is delivered to the user at a speed adequate for consumption in real time. Clearly video tends to place greater demands on the system than audio. The problems are both hardware and software related, although with steadily improving technology it is increasingly feasible to overcome both. E-commerce and payments Everyone is aware of the huge growth of commercial transactions on the Web. The kind of transaction involved can vary widely across simple fixed price retail sales, auctions of various kinds, and reverse auctions for procurement. For retail transactions immediate settlement is usually required, whereas larger scale business to business transactions are usually handled through relatively traditional invoicing methods. Even those are tending to be altered towards paperless billing and payment schemes that cut transaction costs to a minimum. Systems for e-commerce vary enormously in their sophistication from simple requests for payment using a PayPal button to highly sophisticated Web operations such as Amazon and eBay. Open source PHP software exists to cover a significant part of this spectrum, some of it in the form of extensions to CMS frameworks. PayPal has achieved a very high profile, especially with smaller operators, by offering easy access for merchants combined with technology that is relatively simple to implement. This includes the ability to complete a transaction with online confirmation in a way that is suitable for the sale of electronically deliverable goods such as software. Clearly, robust authentication of users is essential for e-commerce. For all but the simplest transactions, some kind of shopping cart is highly desirable. These requirements imply a need for good session handling, preferably taking effect as soon as a visitor arrives at a site. Nearly every shopping site will allow a visitor to accumulate items in a shopping cart prior to any kind of login. There is a plethora of payment systems, some of them suitable mainly for large volume uses, but others that can be applied on a small scale. A particular CMS framework might adopt some standard payment mechanisms that are then integral to the CMS and can be used whenever needed. Security is obviously paramount, as loss of data is both financially damaging and extremely bad for the site's reputation. E-commerce sites also often use a number of the features described in other sections here. A popular addition is the ability for customers to review the items they have purchased. This kind of facility may lead to further requirements to distinguish categories of users so as to give incentives to people who regularly write reviews.
Read more
  • 0
  • 0
  • 1324

article-image-real-content-php5-cms-part-2
Packt
27 Oct 2009
8 min read
Save for later

Real Content in PHP5 CMS: Part 2

Packt
27 Oct 2009
8 min read
Framework solution To explore implementation details, we will look at an example that is simple enough to be shown in some detail. It is an application for handling pages composed largely of text and images. After studying the example, we will consider how the application could be made more advanced. A simple text application Here, we'll look at a component that can be used on its own but is also intended as a starting point for more sophisticated uses. Its essence is that it handles a piece of text, created using the site WYSIWYG editor by the administrator. The text can be displayed as the main portion of a Web page. Ancillary information is held about the text. Any particular text can be the target of a menu entry, so the component can be used for simple pages. The WYSIWYG editor provides for moderately complex text layout and the inclusion of images. We shall see that writing a text handling extension is made very much simpler by the various elements of the CMS framework. The database table for simple text After the ID number that is used as the main key we have the primary constituents of a piece of text. They are the headline, the subheading, and the body of the article. Each of these will simply reflect whatever text is put in them by the author, who in this simple implementation must also be an administrator. Next we have a couple of time stamps that can be automatically maintained by the software. Rather obviously, the created time stamp is set when the row is created, and the modified time stamp is set every time the row is updated. We then have fields that control the publication of the text. First, there is a simple indicator, which is set to zero if the text is not published and is set to one if it is published. When set to unpublished, the indicator overrides the start and end dates, if they are present. If a non-zero start date is set, then the text will not be published before that date. Likewise, if a non-zero finish date is set, the article will cease to be published after that date. Publishing dates are very useful to control when text will appear as it is often helpful to time the start of publication, and it creates a bad impression if obsolete text is not removed. Then we have data that describes who has worked on the text. The original creator is recorded as a user ID, and the last modifier is likewise recorded as a user ID. These fields are intended for tracking what is happening to the text rather than for display. On the other hand, the byline is entirely for display. Version is a character field that has no defined structure in this simple component, but could be elaborated in many different ways. Storage for metadata is provided as keys and description. This information is not for display on the browser page, but is used to generate meta information in the header of a page containing the text. Tags containing metadata can influence search engines used for indexing of pages, although description is much more influential than keywords, which are believed to be largely disregarded. Finally, a hit counter is automatically maintained by the system, being set initially to zero and then updated every time the text is shown to a site visitor. A text data object When a text item is loaded into memory from the database, a class provides for the definition of the object will be created. For the simple text application, the class is: class textItem extends aliroDatabaseRow { protected $DBclass = 'aliroDatabase'; protected $tableName = '#__simple_text'; protected $rowKey = 'id'; public function store ($updateNulls=false) { $userid = aliroUser::getInstance()->id; if ($this->id) { $this->modified = date('Y-m-d H:i:s'); $this->modify_id = $userid; } else { $ this->created = date('Y-m-d H:i:s'); $this->author_id = userid; } parent::store($updateNulls); } } Much of the hard work is done in the parent class, aliroDatabaseRow. Because the database framework derives information from the database itself, there is no need to specify the fields that are in the table, which makes it easier to cope with future changes. The minimum that has to be done is to specify the name of the singleton database class, the name of the table (using a symbol in place of the actual prefix), and to define the name of the primary key field. In this case, the store method is also extended. This provides an easy way to maintain the time stamps on the text. The current user is found through the aliroUser singleton class. We know whether a text row is new from whether it already has a value for id. The correct date and user field can then be updated. Finally, the standard store method in the parent class is invoked. Administering text items—controller The administrator logic for handling simple text follows the usual pattern of first providing a list of items, paged if necessary, then allowing more detailed access to individual items, including the ability to edit. Logic for overall control is provided by the aliroComponentAdminManager class, and the aliroComponentAdminControllers class. In fact, we could nominate in the packaging XML aliroComponentAdminManager as the adminclass for our component, since the dedicated textAdmin class does nothing: class textAdmin extends aliroComponentAdminManager { // This could be omitted - included here in case extra code needs to be added public function __construct ($component, $system, $version) { parent::__construct ($component, $system, $version); } // Likewise, this could be omitted unless extra code is needed public function activate () { parent::activate(); } } Why might we want to write a dedicated extension to aliroComponentAdminManager? Well, this is the common entry point for the administrator side of our component, so if we wanted any processing to exist that could affect every use of the component, this is the place to put it. The two possible locations are the constructor and the activation method. The constructor receives information from the CMS environment in the form of a component object (describing this component), the name of the system that is calling us, and its version. It is invoked as soon as the correct component has been determined. The standard processing in the aliroComponentAdminManager constructor includes creating the controller class, and acquiring some common variables from $_REQUEST. Once setup is completed, the activation method is invoked without any parameters. The activate method of the aliroComponentAdminManager class strips any magic quotes, and decides what method to call. Of course, the framework allows us to construct a component completely differently if we choose. The only constraint is that we must write a class whose name is given in the packaging XML, and provide it with an activate method. But usually it is a lot easier to follow the standard construction, and the bare bones of a new component can be built and downloaded online from http://developer.aliro.org. Nothing specific has been done yet, and we have to move into the controller code before we can find anything to do with handling text objects. The controller is subclassed from aliroComponentAdminControllers and starts off as shown: class textAdminText extends aliroComponentAdminControllers { private static $instance = null; // If no code is needed in the constructor, it can be omitted, relying on the parent class protected function __construct ($manager) { parent::__construct ($manager); } public static function getInstance ($manager) { return is_object(self::$instance) ? self::$instance : (self::$instance = new self ($manager)); } public function getRequestData () { // Get information from $_POST or $_GET or $_REQUEST // This method will be called before the toolbar method } // If this method is provided, it should return true if permission test is satisfied, false otherwise public function checkPermission () { $authoriser = aliroAuthoriser::getInstance(); if ($test = $authoriser->checkUserPermission('manage', 'aSimpleText', '*')) { if (!$this->idparm) return true; if ($authoriser->checkUserPermission('edit', 'aSimpleText', $this->idparm)) return true; } return false; } Here, the constructor is not needed; it is shown only to indicate the possibility of having code at the point the controller object is created. The constructor receives the manager object as a parameter, in this case an instance of textAdmin, a subclass of aliroComponentAdminManager. The controller is a singleton class, and here a form of the getInstance method is shown that can be used completely unchanged from component to component. Then we have two methods that are standard. Neither has to be provided, and in this case, the getRequestData method is not needed since it does nothing. Its purpose is to run early on (it is called before the toolbar processing and well before the processing specific to the current request) to acquire information from $_REQUEST or $_GET or $_PUT (or possibly other super-globals). They can be saved as object properties so as to be available for toolbar construction or other processing. The checkPermission method provides the component with a way to easily control who is able to access its facilities. If the method returns true then the user will be allowed to continue, but if it returns false, they will be refused access. In this example, there is always a check that the user is permitted to manage objects of the type aSimpleText and if a specific one is identified by its ID, then there is a further check that the user is permitted to edit that particular text item.
Read more
  • 0
  • 0
  • 1462

article-image-real-content-php5-cms-part-3
Packt
27 Oct 2009
8 min read
Save for later

Real Content in PHP5 CMS: Part 3

Packt
27 Oct 2009
8 min read
Administering text items—viewer Generating the XHTML is handled in a separate class, thus implementing the principles of the MVC pattern. The viewer class constructor establishes strings for translation in a way that will allow them to be picked up by gettext, as well as invoking the constructor in the parent class basicAdminHTML, which will provide useful methods and also transfer information such as the page navigation object from the controller object passed as a parameter: class listTextHTML extends basicAdminHTML { public function __construct ($controller) { parent::__construct($controller); $lang_strings = array(T_('Simple Text'),T_('Title'), T_('Byline'),T_('Version'), T_('Publishing'),T_('Published'), T_('Start date'),T_('End date'), T_('Article text'),T_('Metadata'), T_('Keys'),T_('Description'), T_('Hits'),T_('ID')); $this->translations = array_combine( $lang_strings, $lang_strings); } The actual display of a list of text items is then quite simple, involving the creation of a heading first, followed by a loop through the text items, and then some final XHTML including hidden fields that allow for effective navigation. Note that the parent class will have set up $this->optionurl and $this->optionline to help in the construction of links within the component and a hidden variable to identify the component respectively. public function view ($rows) { $mainhtml = $this->listview($rows); echo <<<ALL_HTML $mainhtml <div> <input type="hidden" name="task" value="" /> $this->optionline <input type="hidden" name="boxchecked" value="0" /> <input type="hidden" name="hidemainmenu" value="0" /> </div>ALL_HTML; } The view method does very little, relying on the listview method for most of the work, and only adding hidden fields needed to ensure that navigation and the toolbar will work correctly. Note that the parent class helps us by setting $this->optionline with a hidden input field for the critical option variable needed to ensure the correct component is invoked when the form is submitted. Actual XHTML form tags are created by the CMS framework so that every administrator page is a form. The reason for splitting the page creation in this way will become apparent later, when we look at menu creation. So, moving on to the listview method, we find quite a lot of simple code, which is mainly just a definition of the page in XHTML. The second and third parameters will be set differently from their default values when we come to menu creation. public function listview ($rows, $showlinks=true, $subhead='') { $rowcount = count($rows); $html = <<<ADMIN_HEADER {$this->header($subhead)} <table class="adminlist" width="100%"> <thead> <tr> <th width="3%" class="title"> <input type="checkbox" name="toggle" value="" onclick="checkAll($rowcount);" /> </th> <th> {$this->T_('ID')} </th> <th width="50%" class="title"> {$this->T_('Title')} </th> <th> {$this->T_('Byline')} </th> <th> {$this->T_('Hits')} </th> <th align="left"> {$this->T_('Published')} </th> </tr> </thead> <tbody>ADMIN_HEADER; $i = $k = 0; foreach ($rows as $i=>$row) { if ($showlinks) $title = <<<LINK_TITLE <a href="{$this->optionurl}&amp;task=edit&amp; id=$row->id">$row->title</a>LINK_TITLE; else $title = $row->title; $html .= <<<END_OF_BODY_HTML <tr class="row$k"> <td> {$this->html('idBox', $i, $row->id)} </td> <td align="center"> $row->id </td> <td> $title </td> <td> $row->byline </td> <td align="center"> $row->hits </td> <td align="center"> {$this->html('publishedProcessing', $row, $i )} </td> </tr>END_OF_BODY_HTML; $i++; $k = 1 - $k; } if (0 == $rowcount) $html .= <<<NO_ITEMS_HTML <tr><td colspan="6" class="center"> {$this->T_('No items')} </td></tr>NO_ITEMS_HTML; $html .= <<<END_OF_FINAL_HTML </tbody> </table> {$this->pageNav->getListFooter()}END_OF_FINAL_HTML; return $html; } When it comes to adding a new item or editing an existing one, no looping is required, and the WYSIWYG editor is activated to provide a helpful interface for the administrator who is editing a text item. Note that the use of PHP heredoc allows the XHTML to be written out quite plainly, with the PHP insertions unobtrusive but effective. Actual text for translation is shown in its correct place (in the base language) by using the T_ method that is inherited from aliroBasicHTML via basicAdminHTML. public function edit ($text) { $subhead = $text->id ? 'ID='.$text->id : T_('New'); $editor = aliroEditor::getInstance(); echo <<<EDIT_HTML {$this->header($subhead)} <div id="simpletext1"> <div> <label for="title">{$this->T_('Title')}</label><br /> <input type="text" name="title" id="title" size="80" value="$text->title" /> </div> <div> <label for="byline">{$this->T_('Byline')}</label><br /> <input type="text" name="byline" id="byline" size="80" value="$text->byline" /> </div> <div> <label for="version">{$this->T_('Version')}</label><br /> <input type="text" name="version" id="version" size="80" value="$text->version" /> </div> <div> <label for="article">{$this->T_('Article text')}</label><br /> {$editor->editorAreaText( 'article', $text->article, 'article', 500, 200, 80, 15 )} </div> </div> <div id="simpletext2"> <fieldset> <legend>{$this->T_('Publishing')}</legend> <div> <label for="published">{$this->T_('Published')}</label><br /> <input type="checkbox" name="published" id="published" value="1" {$this->checkedIfTrue($text->published)} /> </div> <div> <label for="publishstart">{$this->T_('Start date')}</label><br /> <input type="text" name="publish_start" id="publishstart" size="20" value="$text->publish_start" /> </div> <div> <label for="publishend">{$this->T_('End date')}</label><br /> <input type="text" name="publish_end" id="publishend" size="20" value="$text->publish_end" /> </div> </fieldset> <fieldset> <legend>{$this->T_('Metadata')}</legend> <div> <label for="metakey">{$this->T_('Keys')}</label><br /> <textarea name="metakey" id="metakey" rows="4" cols="40">$text->metakey</textarea> </div> <div> <label for="metadesc">{$this->T_('Description')}</label><br /> <textarea name="metadesc" id="metadesc" rows="4" cols="40">$text->metadesc</textarea> </div> </fieldset> <input type="hidden" name="task" value="" /> $this->optionline </div> <div id="simpletext3"> <input type="hidden" name="id" value="$text->id" /> <input type="hidden" name="boxchecked" value="0" /> <input type="hidden" name="hidemainmenu" value="0" /> </div>EDIT_HTML; } Finally, there is a common method to deal with the creation of the heading. It uses the addCSS method provided by the parent class to link to a small amount of CSS that is held in a separate file. Although the list of text items defined in the XHTML above is perfectly legitimate as a table, since it really is a tabular structure, the heading would be better built out of other XHTML elements. The only reason for using a table here is that it is one of the features retained from earlier systems for the sake of backwards compatibility: private function header ($subhead='') { $this->addCSS(_ALIRO_ADMIN_DIR.'/components /com_text/admin.text.css'); if ($subhead) $subhead = "<small>[$subhead]</small>"; return <<<HEAD_HTML <table class="adminheading"> <tr> <th class="user"> {$this->T_('Simple Text')} $subhead </th> </tr> </table>HEAD_HTML; } }
Read more
  • 0
  • 0
  • 1863
article-image-soap-and-php-5
Packt
22 Oct 2009
16 min read
Save for later

SOAP and PHP 5

Packt
22 Oct 2009
16 min read
SOAP SOAP, formerly known as Simple Object Access Protocol (until the acronym was dropped in version 1.2), came around shortly after XML-RPC was released. It was created by a group of developers with backing from Microsoft. Interestingly, the creator of XML-RPC, David Winer, was also one of the primary contributors to SOAP. Winer released XML-RPC before SOAP, when it became apparent to him that though SOAP was still a way away from being completed, there was an immediate need for some sort of web service protocol. Like XML-RPC, SOAP is an XML-based web service protocol. SOAP, however, satisfies a lot of the shortcomings of XML-RPC: namely the lack of user-defined data types, better character set support, and rudimentary security. It is quite simply, a more powerful and flexible protocol than REST or XML-RPC. Unfortunately, sacrifices come with that power. SOAP is a much more complex and rigid protocol. For example, even though SOAP can stand alone, it is much more useful when you use another XML-based standard, called Web Services Descriptor Language (WSDL), in conjunction with it. Therefore, in order to be proficient with SOAP, you should also be proficient with WSDL. The most-levied criticism of SOAP is that it is overly complex. Indeed, SOAP is not simple. It is long and verbose. You need to know how namespaces work in XML. SOAP can rely heavily on other standards. This is true for most implementations of SOAP, including Microsoft Live Search, which we will be looking at. The most common external specifications used by a SOAP-based service is WSDL to describe its available services, and that, in turn, usually relies on XML Schema Data (XSD) to describe its data types. In order to "know" SOAP, it would be extremely useful to have some knowledge of WSDL and XSD. This will allow one to figure out how to use the majority of SOAP services. We are going to take a "need to know" approach when looking at SOAP. Microsoft Live Search's SOAP API uses WSDL and XSD, so we will take a look at SOAP with the other two in mind. We will limit our discussion on how to gather information about the web service that you, as a web service consumer, would need and how to write SOAP requests using PHP 5 against it. Even though this article will just introduce you to the core necessities of SOAP, there is a lot of information and detail. SOAP is very meticulous and you have to keep track of a fair amount of things. Do not be discouraged, take notes if you have to, and be patient. All three, SOAP, WSD, and XSD are maintained by the W3C. All three specifications are available for your perusal. The official SOAP specification is located at http://www.w3.org/TR/soap/. WSDL specification is located at http://www.w3.org/TR/wsdl. Finally, the recommended XSD specification can be found at http://www.w3.org/XML/Schema. Web Services Descriptor Language (WSDL) With XML Schema Data (XSD) Out of all the drawbacks of XML-RPC and REST, there is one that is prominent. Both of these protocols rely heavily on good documentation by the service provider in order to use them. Lacking this, you really do not know what operations are available to you, what parameters you need to pass in order to use them, and what you should expect to get back. Even worse, an XML-RPC or REST service may be poorly or inaccurately documented and give you inaccurate or unexpected results. SOAP addresses this by relying on another XML standard called WSDL to set the rules on which web service methods are available, how parameters should be passed, and what data type might be returned. A service's WSDL document, basically, is an XML version of the documentation. If a SOAP-based service is bound to a WSDL document, and most of them are, requests and responses must adhere to the rules set in the WSDL document, otherwise a fault will occur. WSDL is an acronym for a technical language. When referring to a specific web service's WSDL document, people commonly refer to the document as "the WSDL" even though that is grammatically incorrect. Being XML-based, this allows clients to automatically discover everything about the functionality of the web service. Human-readable documentation is technically not required for a SOAP service that uses a WSDL document, though it is still highly recommended. Let's take a look at the structure of a WSDL document and how we can use it to figure out what is available to us in a SOAP-based web service. Out of all three specifications that we're going to look at in relationship to SOAP, WSDL is the most ethereal. Both supporters and detractors often call writing WSDL documents a black art. As we go through this, I will stress the main points and just briefly note other uses or exceptions. Basic WSDL Structure Beginning with a root definitions element, WSDL documents follow this basic structure:     <definitions>        <types>        …        </types>        <message>        …        </message>        <portType>        …        </portType>        <binding>        …        </binding>    </definitions> As you can see, in addition to the definitions element, there are four main sections to a WSDL document: types, message, portType, and binding. Let's take a look at these in further detail. Google used to provide a SOAP service for their web search engine. However, this service is now deprecated, and no new developer API keys are given out. This is unfortunate because the service was simple enough to learn SOAP quickly, but complex enough to get a thorough exposure to SOAP. Luckily, the service itself is still working and the WSDL is still available. As we go through WSDL elements, we will look at the Google SOAP Search WSDL and Microsoft Live Search API WSDL documents for examples. These are available at http://api.google.com/GoogleSearch.wsdl and http://soap.search.msn.com/webservices.asmx?wsdl respectively. definitions Element This is the root element of a WSDL document. If the WSDL relies on other specifications, their namespace declarations would be made here. Let's take a look at Google's WSDL's definition tag:     <definitions name="GoogleSearch"        targetNamespace="urn:GoogleSearch"                                                > The more common ones you'll run across are xsd for schema namespace, wsdl for the WSDL framework itself, and soap and soapenc for SOAP bindings. As these namespaces refer to W3C standards, you will run across them regardless of the web service implementation. Note that some searches use an equally common prefix, xs, for XML Schema. tns is another common namespace. It means "this namespace" and is a convention used to refer to the WSDL itself. types Element In a WSDL document, data types used by requests and responses need to be explicitly declared and defined. The textbook answer that you'll find is that the types element is where this is done. In theory, this is true. In practice, this is mostly true. The types element is used only for special data types. To achieve platform neutrality, WSDL defaults to, and most implementations use, XSD to describe its data types. In XSD, many basic data types are already included and do not need to be declared. Common Built-in XSD Data Types Time Date Boolean String Base64Binary Float Double Integer Byte For a complete list, see the recommendation on XSD data types at http://www.w3.org/TR/xmlschema-2/. If the web service utilizes nothing more than these built-in data types, there is no need to have special data types, and thus, types will be empty. So, the data types will just be referred to later, when we define the parameters. There are three occasions where data types would be defined here: If you want a special data type that is based on a built-in data type. Most commonly this is a built-in, whose value is restricted in some way. These are known as simple types. If the data type is an object, it is known as a complex type in XSD, and must be declared. An array, which can be described as a hybrid of the former two. Let's take a look at some examples of what we will encounter in the types element. Simple Type Sometimes, you need to restrict or refine a value of a built-in data type. For example, in a hospital's patient database, it would be ludicrous to have the length of a field called Age to be more than three digits. To add such a restriction in the SOAP world, you would have to define Age here in the types section as a new type. Simple types must be based on an existing built-in type. They cannot have children or properties like complex types. Generally, a simple type is defined with the simpleType element, the name as an attribute, followed by the restriction or definition. If the simple type is a restriction, the built-in data type that it is based on, is defined in the base attribute of the restriction element. For example, a restriction for an age can look like this:     <xsd:simpleType name="Age">        <xsd:restriction base="xsd:integer">            <xsd:totalDigits value="3" />        </xsd:restriction>    </xsd:simpleType> Children elements of restriction define what is acceptable for the value. totalDigits is used to restrict a value based on the character length. A table of common restrictions follows: Restriction Use Applicable In enumeration Specifies a list of acceptable values. All except boolean fractionDigits Defines the number of decimal places allowed. Integers length Defines the exact number of characters allowed. Strings and all binaries maxExclusive/ maxInclusive Defines the maximum value allowed. If Exclusive is used, value cannot be equal to the definition. If Inclusive, can be equal to, but not greater than, this definition. All numeric and dates minLength/ maxLength Defines the minimum and maximum number of characters or list items allowed. Strings and all binaries minExclusive/ minInclusive Defines the minimum value allowed. If Exclusive is used, value cannot be equal to the definition. If Inclusive, can be equal to, but not less than, this definition. All numeric and dates pattern A regular expression defining the allowed values. All totalDigits Defines the maximum number of digits allowed. Integers whiteSpace Defines how tabs, spaces, and line breaks are handled. Can be preserve (no changes), replace (tabs and line breaks are converted to spaces) or collapse (multiple spaces, tabs, and line breaks are converted to one space. Strings and all binaries A practical example of a restriction can be found in the MSN Search Web Service WSDL. Look at the section that defines SafeSearchOptions.     <xsd:simpleType name="SafeSearchOptions">        <xsd:restriction base="xsd:string">            <xsd:enumeration value="Moderate" />            <xsd:enumeration value="Strict" />            <xsd:enumeration value="Off" />    </xsd:restriction> </xsd:simpleType> In this example, the SafeSearchOptions data type is based on a string data type. Unlike a regular string, however, the value that SafeSearchOptions takes is restricted by the restriction element. In this case, the several enumeration elements that follow. SafeSearchOptions can only be what is given in this enumeration list. That is, SafeSearchOptions can only have a value of "Moderate", "Strict", or "Off". Restrictions are not the only reason to use a simple type. There can also be two other elements in place of restrictions. The first is a list. If an element is a list, it means that the value passed to it is a list of space-separated values. A list is defined with the list element followed by an attribute named itemType, which defines the allowed data type. For example, this example specifies an attribute named listOfValues, which comprises all integers.     <xsd:simpleType name="listOfValues">        <xsd:list itemType="xsd:integer" />    </xsd:simpleType> The second is a union. Unions are basically a combination of two or more restrictions. This gives you a greater ability to fine-tune the allowed value. Back to our age example, if our service was for a hospital's pediatrics ward that admits only those under 18 years old, we can restrict the value with a union.     <xsd:simpleType name="Age">        <xsd:union>            <xsd:simpleType>                <xsd:restriction base="decimal">                        <xsd:minInclusive value="0" />                </xsd:restriction>            </xsd:simpleType>            <xsd:simpleType>                <xsd:restriction base="decimal">                        <xsd:maxExclusive value="18" />                </xsd:restriction>            </xsd:simpleType>        </xsd:union>    </xsd:simpleType> Finally, it is important to note that while simple types are, especially in the case of WSDLs, used mainly in the definition of elements, they can be used anywhere that requires the definition of a number. For example, you may sometimes see an attribute being defined and a simple type structure being used to restrict the value. Complex Type Generically, a complex type is anything that can have multiple elements or attributes. This is opposed to a simple type, which can have only one element. A complex type is represented by the element complexType in the WSDL. The most common use for complex types is as a carrier for objects in SOAP transactions. In other words, to pass an object to a SOAP service, it needs to be serialized into an XSD complex type in the message. The purpose of a complexType element is to explicitly define what other data types make up the complex type. Let's take a look at a piece of Google's WSDL for an example:     <xsd:complexType name="ResultElement">        <xsd:all>            <xsd:element name="summary" type="xsd:string"/>            <xsd:element name="URL" type="xsd:string"/>            <xsd:element name="snippet" type="xsd:string"/>            <xsd:element name="title" type="xsd:string"/>            <xsd:element name="cachedSize" type="xsd:string"/>            <xsd:element name=                        "relatedInformationPresent" type="xsd:boolean"/>            <xsd:element name="hostName" type="xsd:string"/>            <xsd:element name=                        "directoryCategory" type="typens:DirectoryCategory"/>            <xsd:element name="directoryTitle" type="xsd:string"/>        </xsd:all>    </xsd:complexType> First thing to notice is how the xsd: namespace is used throughout types. This denotes that these elements and attributes are part of the XSD specification. In this example, a data type called ResultElement is defined. We don't exactly know what it is used for right now, but we know that it exists. An element tag denotes complex type's equivalent to an object property. The first property of it is summary, and the type attribute tells us that it is a string, as are most properties of ResultElement. One exception is relatedInformationPresent, which is a Boolean. Another exception is directoryCategory. This has a data type of DirectoryCategory. The namespace used in the type attribute is typens. This tells us that it is not an XSD data type. To find out what it is, we'll have to look for the namespace declaration that declared typens.
Read more
  • 0
  • 0
  • 4313

article-image-testing-help-system-java-application
Packt
22 Oct 2009
6 min read
Save for later

Testing a HELP System in a Java Application

Packt
22 Oct 2009
6 min read
Introduction {literal}As more and more features get added to your software, the Help system for the software becomes extensive. It could probably contains hundreds of HTML files plus a similar number of images. We could always face the problems listed below. There could be broken links in the help index. Some files may not be listed in the index, therefore they can not be read by the customers. Some of the contextual help buttons could show the wrong topic. Some of the HTML files can contain broken links or incorrect image tags. Not all of the file titles would match their index entry. Another problem could occur when the user does a free text search of the help system. The result of such a search is a list of files, each represented by its title. In our system,  documents could have the title "untitled". In fact, the JavaHelp 2.0 System User's Guide contains the recommendation "To avoid confusion, ensure that the <TITLE> tag corresponds to the title used in the table of contents." Given that customers mostly use the Help system when they are already frustrated by our software, we should always see to it that such errors do not exist in our help system. To do this, we will write a tool, HelpGenerator, that generates some of the boilerplate XML in the help system and checks the HTML and index files for the problems listed above. We will also build tools for displaying and testing the contextual help. We've re-engineered and improved these tools and present them in this article. In this article we are assuming familiarity with the JavaHelp system. Documentation and sample code for JavaHelp can be found at: http://java.sun.com/products/javahelp. Overview A JavaHelp package consists of: A collection of HTML and image files containing the specific Help information to be displayed. A file defining the index of the Help topics. Each index item in the file consists of the text of the index entry and a string representing the target of the HTML file to be displayed for that index entry, for example: <index version="1.0"> <indexitem text="This is an example topic."target="Topic"> <indexitem text="This is an sub-topic."target="SubTopic"/> </indexitem></index> A file associating each target with its corresponding HTML file (or more generally, a URL)—the map file. Each map entry consists of the target name and the URL it is mapped to, for example: <map version="1.0"> <mapID target="Topic" url="Topic.html"/> <mapID target="SubTopic" url="SubTopic.html"/></map> A HelpSet file (by default HelpSet.hs) which specifies the names of the index and map files and the folder containing the search database. Our software will normally have a main menu item to activate the Help and, in addition, buttons or menu items on specific dialogs to activate a Help page for a particular topic, that is, "context-sensitive" Help. What Tests Do We Need?? At an overall structural level, we need to check: For each target referred to in the index file, is there a corresponding entry in the map file? In the previous example, the index file refers to targets called Topic and SubTopic. Are there entries for these targets in the map file? For each URL referred to in the map file, is that URL reachable? In the example above, do the files Topic.html and SubTopic.html exist? Are there HTML files in our help package which are never referred to? If a Help button or menu item on some dialog or window is activated, does the Help facility show the expected topic? If the Help search facility has been activated, do the expected search results show? That is, has the search database been built on the latest versions of our Help pages? At a lower level, we need to check the contents of each of the HTML files: Do the image tags in the files really point to images in our help system? Are there any broken links? Finally, we need to check that the contents of the files and the indexes are consistent Does the title of each help page match its index? To simplify these tests, we will follow a simple naming pattern as follows: We adopt the convention that the name of each HTML file should be in CamelCase format (conventional Java class name format) plus the .html extension. Also, we use this name, without the extension, as the target name. For example, the target named SubTopic will correspond to the file SubTopic.html. Furthermore, we assume that there is a single Java package containing all the required help files, namely, the HTML files, the image files, the index file, and the map file. Finally, we assume a fixed location for the Help search database. With this convention, we can now write a program that: Generates the list of available targets from the names of the HTML files. Checks that this list is consistent with the targets referred to in the index file. Checks that the index file is well-formed in that: It is a valid XML document. It has no blank index entries. It has no duplicate index entries. Each index entry refers to a unique target. Generates the map file, thereby guaranteeing that it will be consistent with the index file and the HTML files. The class HelpGenerator in the package jet.testtools.help does all this,and, if there are no inconsistencies found, it generates the map file. If an inconsistency or other error is found, an assertion will be raised. HelpGenerator also performs the consistency checks at the level of individual HTML files. Let's look at some examples. An HTML File That is Not Indexed Here is a simple help system with just three HTML files: The index file, HelpIndex.xml, only lists two of the HTML files: <index version="1.0"> <indexitem text="This is an example topic." target="ExampleTopic"> <indexitem text="This is an example sub-topic." target="ExampleSubTopic"/> </indexitem></index> When we run HelpGenerator over this system (we'll see how to do this later in this article), we get an assertion with the error messageThe Help file: TopicWithoutTarget.html was not referenced in the Index file: HelpIndex.xml.
Read more
  • 0
  • 0
  • 2364

article-image-developing-joomla-component-and-understanding-its-structure
Packt
22 Oct 2009
3 min read
Save for later

Developing the Joomla! Component and Understanding its Structure

Packt
22 Oct 2009
3 min read
Joomla!'s Component Structure Joomla! employs a specific naming scheme, which is used by all components. Each component in the system has a unique name with no spaces. The code is split into two folders, each bearing the component name prefixed by com_. The component used here will be called reviews. Therefore, you will have to create two folders named com_reviews: Create one in the folder named components for the front end. Create one in the folder named components within the administrator folder for the back end. When the component is loaded from the front end, Joomla! will look for a file with the component's unique name ending in a .php extension. Within the components/com_reviews folder, create the reviews.php file. Similarly, running it in the back end assumes the presence of a file prefaced with admin. followed by the component name and ending in .php. Add the file admin.reviews.php in administrator/components/com_reviews. Leave both the files empty for the moment. Executing the Component All front-end requests in Joomla! go through index.php in the root directory. Different components can be loaded by setting the option variable in the URL GET string. If you install Joomla! on a local web server in a directory titled joomla, the URL for accessing the site will be http://localhost/joomla/index.php or something similar. Assuming this is the case, you can load the component's front end by opening http://localhost/joomla/index.php?option=com_reviews in your browser. At this point, the screen should be essentially blank, apart from the common template elements and modules. To make this component slightly more useful, open reviews.php and add the following code, then refresh the browser: <?php defined( '_JEXEC' ) or die( 'Restricted access' ); echo '<div class="componentheading">Restaurant Reviews</div>'; ?> Your screen will look similar to the following: You may be wondering why we called defined() at the beginning of the file. This is a check to ensure that the code is called through Joomla! instead of being accessed directly at components/com_reviews/reviews.php. Joomla! automaticallyconfigures the environment with some security safeguards that can be defeated ifsomeone is able to directly execute the code for your component. For the back end, drop this code into administrator/components/com_reviews/admin.reviews.php: <?php defined( '_JEXEC' ) or die( 'Restricted access' ); echo 'Restaurant Reviews'; ?> Go to http://localhost/joomla/administrator/index.php?option=com_reviews and compare your result to this:   Joomla!'s Division between Front End and Back End For all Joomla! components, code empowering the back-end portion is kept away from the front-end code. In some instances, such as the database table class, the back end will use certain files from the front end, but otherwise the two are separate. Security is enhanced as you are less likely to slip the administrative functions into the front-end code. This is an important distinction as the front end and back end are similar in structure. The following folder diagram shows the Joomla! root with the administrator folder expanded: Notice that the administrator folder has a structure similar to the root folder. It is important to differentiate between the two, else you may place your code in the wrong folder and it will fail to execute until youmove it.
Read more
  • 0
  • 0
  • 4387
article-image-module-development-joomla
Packt
22 Oct 2009
4 min read
Save for later

Module Development in Joomla

Packt
22 Oct 2009
4 min read
Introduction Modules in Joomla can be used to fetch and display data almost anywhere on a page in a website.In this article, we will cover the following topics on module development. Registering the module in the database Getting and setting parameters Centralizing data access and output using helper classes Selecting display options using layouts Displaying the latest reviews Displaying a random review We will assume that we have a table in our database called jos_modules with the following fields title, ordering, position, published, module, showtitle, and params. We will also assume that we have a website that reviews different restaurants. However, visitors have to go to the component to see the reviews. We would be developing a module so that we could pull the content directly from the reviews and display them. Registering the Module in the Database As with the component, we will have to register the module in the database so that it can be referenced in the back end and used effectively. Entering a record into the jos_modules table will take care of this. Open your database console and enter the following query: INSERT INTO jos_modules (title, ordering, position, published, module, showtitle, params) VALUES ('Restaurant Reviews', 1, 'left', 1, 'mod_reviews', 1, 'style=simplenitems=3nrandom=1'); If you're using phpMyAdmin, enter the fields as in the following screen: If you refresh the front end right after entering the record in jos_modules, you'll notice that the module doesn't appear, even though the published column is set to 1. To fix this, go to Extensions | Module Manager in the back end and click the Restaurants Reviews link. Under Menu Assignment, select All and click Save. In the front end, the left-hand side of your front page should look similar to the following: Creating and Configuring a Basic Module Modules are both simple and flexible. You can create a module that simply outputs static text or one that queries remote databases for things like weather reports. Although you can create rather complex modules, they're best suited for displaying data and simple forms. You will not typically use a module for complex record or session management; you can do this through a component or plug-in instead. To create the module for our reviews, we will have to create a directory mod_reviews under /modules. We will also need to create the mod_reviews.php file inside mod_reviews. To start, we'll create a basic module that displays links to the most recent reviews. In the mod_reviews.php file, add the following code: <?php defined('_JEXEC') or die('Restricted access'); $items = $params->get('items', 1); $db =& JFactory::getDBO(); $query = "SELECT id, name FROM #__reviews WHERE published = '1' ORDER BY review_date DESC"; $db->setQuery( $query, 0, $items ); $rows = $db->loadObjectList(); foreach($rows as $row) { echo '<a href="' . JRoute::_('index.php?option=com_reviews&id=' . $row->id . '&task=view') . '">' . $row->name . '</a><br />'; } ?> When you save the file and refresh the homepage, your module should look similar to the following: When the module is loaded, the $params object is pulled into scope and can be used to get and set the parameters. When we added the row into jos_modules, the params column contained three values: one for items (set to 3), one for style (set to simple), and another for random (set to 1). We set $items to the parameter items using the get() member function, defaulting to 1 if no value exists. If desired, you can use the member function set($name, $value) to override or add a parameter for your module. After getting a database object reference, we write a query to select the id and name form jos_reviews and order reverse chronologically by the published date. We use the second and third parameters of setQuery() to generate a LIMIT clause that is automatically added to the query. This ensures that the correct syntax is used for the database type. Once the query is built, we load all the relevant database rows, go through them, and provide a link to each review.
Read more
  • 0
  • 0
  • 2570

article-image-testing-save-dialog-java-using-swing
Packt
21 Oct 2009
10 min read
Save for later

Testing a Save As Dialog in Java using Swing

Packt
21 Oct 2009
10 min read
In this article, we will use an example from our demonstration application, Ikon Do It. The code from this article is in the packages jet.ikonmaker and jet.ikonmaker.test. Click here to download the code. The Ikon Do It 'Save as' Dialog The 'Ikon Do It' application has a Save as function that allows the icon on which we are currently working to be saved with another name. Activating the Save as button displays a very simple dialog for entering the new name. The following figure shows the 'Ikon Do It' Save as dialog. Not all values are allowed as possible new names. Certain characters (such as '*') are prohibited, as are names that are already used. In order to make testing easy, we implemented the dialog as a public class called SaveAsDialog, rather than as an inner class of the main user interface component. We might normally balk at giving such a trivial component its own class, but it is easier to test when written this way and it makes a good example. Also, once a simple version of this dialog is working and tested, it is possible to think of enhancements that would definitely make it too complex to be an inner class. For example, there could be a small status area that explains why a name is not allowed (the current implementation just disables the Ok button when an illegal name is entered, which is not very user-friendly). The API for SaveAsDialog is as follows. Names of icons are represented by IkonName instances. A SaveAsDialog is created with a list of existing IkonNames. It is shown with a show() method that blocks until either Ok or Cancel is activated. If Ok is pressed, the value entered can be retrieved using the name() method. Here then are the public methods: public class SaveAsDialog { public SaveAsDialog( JFrame owningFrame, SortedSet<IkonName> existingNames ) { ... } /** * Show the dialog, blocking until ok or cancel is activated. */ public void show() { ... } /** * The most recently entered name. */ public IkonName name() { ... } /** * Returns true if the dialog was cancelled. */ public boolean wasCancelled() { ... }} Note that SaveAsDialog does not extend JDialog or JFrame, but will use delegation. Also note that the constructor of SaveAsDialog does not have parameters that would couple it to the rest of the system. This means a handler interface is not required in order to make this simple class testable. The main class uses SaveAsDialog as follows: private void saveAs() { SaveAsDialog sad = new SaveAsDialog( frame, store.storedIkonNames() ); sad.show(); if (!sad.wasCancelled()) { //Create a copy with the new name. IkonName newName = sad.name(); Ikon saveAsIkon = ikon.copy( newName ); //Save and then load the new ikon. store.saveNewIkon( saveAsIkon ); loadIkon( newName ); }} Outline of the Unit Test The things we want to test are: Initial settings: The text field is empty. The text field is a sensible size. The Ok button is disabled. The Cancel button is enabled. The dialog is a sensible size. Usability: The Escape key cancels the dialog. The Enter key activates the Ok button. The mnemonics for Ok and Cancel work. Correctness. The Ok button is disabled if the entered name: Contains characters such as '*', '', '/'. Is just white-space. Is one already being used. API test: unit tests for each of the public methods. As with most unit tests, our test class has an init() method for getting an object into a known state, and a cleanup() method called at the end of each test. The instance variables are: A JFrame and a set of IkonNames from which the SaveAsDialog can be constructed A SaveAsDialog, which is the object under test. A UserStrings and a UISaveAsDialog (listed later on) for manipulating the SaveAsDialog with keystrokes. A ShowerThread, which is a Thread for showing the SaveAsDialog. This is listed later on. The outline of the unit test is: public class SaveAsDialogTest { private JFrame frame; private SaveAsDialog sad; private IkonMakerUserStrings = IkonMakerUserStrings.instance(); private SortedSet<IkonName> names; private UISaveAsDialog ui; private Shower shower; ... private void init() { ... } private void cleanup() { ... } private class ShowerThread extends Thread { ... }} UI Helper Methods A lot of the work in this unit test will be done by the static methods in our helper class, UI. Some of these are isEnabled(), runInEventThread(), and findNamedComponent(). The new methods are listed now, according to their function. Dialogs If a dialog is showing, we can search for a dialog by name, get its size, and read its title: public final class UI { ... /** * Safely read the showing state of the given window. */ public static boolean isShowing( final Window window ) { final boolean[] resultHolder = new boolean[]{false}; runInEventThread( new Runnable() { public void run() { resultHolder[0] = window.isShowing(); } } ); return resultHolder[0]; } /** * The first found dialog that has the given name and * is showing (though the owning frame need not be showing). */ public static Dialog findNamedDialog( String name ) { Frame[] allFrames = Frame.getFrames(); for (Frame allFrame : allFrames) { Window[] subWindows = allFrame.getOwnedWindows(); for (Window subWindow : subWindows) { if (subWindow instanceof Dialog) { Dialog d = (Dialog) subWindow; if (name.equals( d.getName() ) && d.isShowing()) { return (Dialog) subWindow; } } } } return null; } /** * Safely read the size of the given component. */ public static Dimension getSize( final Component component ) { final Dimension[] resultHolder = new Dimension[]{null}; runInEventThread( new Runnable() { public void run() { resultHolder[0] = component.getSize(); } } ); return resultHolder[0]; } /** * Safely read the title of the given dialog. */ public static String getTitle( final Dialog dialog ) { final String[] resultHolder = new String[]{null}; runInEventThread( new Runnable() { public void run() { resultHolder[0] = dialog.getTitle(); } } ); return resultHolder[0]; } ...} Getting the Text of a Text Field The method is getText(), and there is a variant to retrieve just the selected text: //... from UI/** * Safely read the text of the given text component. */public static String getText( JTextComponent textComponent ) { return getTextImpl( textComponent, true );}/** * Safely read the selected text of the given text component. */public static String getSelectedText( JTextComponent textComponent ) { return getTextImpl( textComponent, false );}private static String getTextImpl( final JTextComponent textComponent, final boolean allText ) { final String[] resultHolder = new String[]{null}; runInEventThread( new Runnable() { public void run() { resultHolder[0] = allText ? textComponent.getText() : textComponent.getSelectedText(); } } ); return resultHolder[0];} Frame Disposal In a lot of our unit tests, we will want to dispose of any dialogs or frames that are still showing at the end of a test. This method is brutal but effective: //... from UIpublic static void disposeOfAllFrames() { Runnable runnable = new Runnable() { public void run() { Frame[] allFrames = Frame.getFrames(); for (Frame allFrame : allFrames) { allFrame.dispose(); } } }; runInEventThread( runnable );} Unit Test Infrastructure Having seen the broad outline of the test class and the UI methods needed, we can look closely at the implementation of the test. We'll start with the UI Wrapper class and the init() and cleanup() methods. The UISaveAsDialog Class UISaveAsDialog has methods for entering a name and for accessing the dialog, buttons, and text field. The data entry methods use a Cyborg, while the component accessor methods use UI: public class UISaveAsDialog { Cyborg robot = new Cyborg(); private IkonMakerUserStrings us = IkonMakerUserStrings.instance(); protected Dialog namedDialog; public UISaveAsDialog() { namedDialog = UI.findNamedDialog( SaveAsDialog.DIALOG_NAME ); Waiting.waitFor( new Waiting.ItHappened() { public boolean itHappened() { return nameField().hasFocus(); } }, 1000 ); } public JButton okButton() { return (JButton) UI.findNamedComponent( IkonMakerUserStrings.OK ); } public Dialog dialog() { return namedDialog; } public JButton cancelButton() { return (JButton) UI.findNamedComponent( IkonMakerUserStrings.CANCEL ); } public JTextField nameField() { return (JTextField) UI.findNamedComponent( IkonMakerUserStrings.NAME ); } public void saveAs( String newName ) { enterName( newName ); robot.enter(); } public void enterName( String newName ) { robot.selectAllText(); robot.type( newName ); } public void ok() { robot.altChar( us.mnemonic( IkonMakerUserStrings.OK ) ); } public void cancel() { robot.altChar( us.mnemonic( IkonMakerUserStrings.CANCEL ) ); }} A point to note here is the code in the constructor that waits for the name text field to have focus. This is necessary because the inner workings of Swing set the focus within a shown modal dialog as a separate event. That is, we can't assume that showing the dialog and setting the focus within it happen within a single atomic event. Apart from this wrinkle, all of the methods of UISaveDialog are straightforward applications of UI methods. The ShowerThread Class Since SaveAsDialog.show() blocks, we cannot call this from our main thread; instead we spawn a new thread. This thread could just be an anonymous inner class in the init() method: private void init() { //Not really what we do... //setup...then launch a thread to show the dialog. //Start a thread to show the dialog (it is modal). new Thread( "SaveAsDialogShower" ) { public void run() { sad = new SaveAsDialog( frame, names ); sad.show(); } }.start(); //Now wait for the dialog to show...} The problem with this approach is that it does not allow us to investigate the state of the Thread that called the show() method. We want to write tests that check that this thread is blocked while the dialog is showing. Our solution is a simple inner class: private class ShowerThread extends Thread { private boolean isAwakened; public ShowerThread() { super( "Shower" ); setDaemon( true ); } public void run() { Runnable runnable = new Runnable() { public void run() { sad.show(); } }; UI.runInEventThread( runnable ); isAwakened = true; } public boolean isAwakened() { return Waiting.waitFor( new Waiting.ItHappened() { public boolean itHappened() { return isAwakened; } }, 1000 ); }} The method of most interest here is isAwakened(), which waits for up to one second for the awake flag to have been set, this uses a class, Waiting. Another point of interest is that we've given our new thread a name (by the call super("Shower") in the constructor). It's really useful to give each thread we create a name. The init() Method The job of the init() method is to create and show the SaveAsDialog instance so that it can be tested: private void init() { //Note 1 names = new TreeSet<IkonName>(); names.add( new IkonName( "Albus" ) ); names.add( new IkonName( "Minerva" ) ); names.add( new IkonName( "Severus" ) ); names.add( new IkonName( "Alastair" ) ); //Note 2 Runnable creator = new Runnable() { public void run() { frame = new JFrame( "SaveAsDialogTest" ); frame.setVisible( true ); sad = new SaveAsDialog( frame, names ); } }; UI.runInEventThread( creator ); //Note 3 //Start a thread to show the dialog (it is modal). shower = new ShowerThread(); shower.start(); //Note 4 //Wait for the dialog to be showing. Waiting.waitFor( new Waiting.ItHappened() { public boolean itHappened() { return UI.findNamedFrame( SaveAsDialog.DIALOG_NAME ) != null; } }, 1000 ); //Note 5 ui = new UISaveAsDialog();} Now let's look at some of the key points in this code. Note 1: In this block of code we create a set of IkonNames with which our SaveAsDialog can be created. Note 2: It's convenient to create and show the owning frame and create the SaveAsDialog in a single Runnable. An alternative would be to create and show the frame with a UI call and use the Runnable just for creating the SaveAsDialog. Note 3: Here we start our Shower, which will call the blocking show() method of SaveAsDialog from the event thread. Note 4: Having called show() via the event dispatch thread from our Shower thread, we need to wait for the dialog to actually be showing on the screen. The way we do this is to search for a dialog that is on the screen and has the correct name. Note 5: Once the SaveAsDialog is showing, we can create our UI Wrapper for it. The cleanup() Method The cleanup() method closes all frames in a thread-safe manner: private void cleanup() { UI.disposeOfAllFrames();}
Read more
  • 0
  • 0
  • 2964