Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Events
Videos
Audiobooks
Packt Hub
Free Learning
Arrow right icon
timer SALE ENDS IN
0 Days
:
00 Hours
:
00 Minutes
:
00 Seconds

How-To Tutorials

7019 Articles
article-image-drupal-6-performance-optimization-using-views-and-panels-caching
Packt
19 Feb 2010
5 min read
Save for later

Drupal 6 Performance Optimization Using Views and Panels Caching

Packt
19 Feb 2010
5 min read
Views caching The Views 2 module allows you to cache your Views data and content. You can cache Views data per View. We're going to enable caching on one of our existing Views, and also create a brand new View and set caching for that as well using the test content we just generated. This will show you a nice integration of the Devel functionality with the Views module and then how caching works with Views. Go to your Site building | Views configuration page and you'll see many of your default and custom views listed. We have a view on this site for our main photo gallery. The view is named photo_gallery in our View listing. Go ahead and click on one of your Views edit links to get into edit mode for a View. In our Views 2 interface mode, we'll see our tabs for default, Page, and/or Block View display. I'm going to click on my Page tab to see my View's page settings. Under my Basic settings configuration, I'll see a link for Caching. Currently, our Caching link states None, meaning that no caching has been configured for this view. Click on the None link. Select the Time-based radio button. This will enable Time-based caching for our View page. Click the Update default display button. The next caching options configuration screen will ask you to set the amount of time for both, your View Query results and for your View Rendered output. Query results refer to the amount of time raw queries should be cached. Rendered output is the amount of time the View HTML output should be cached. So basically, you can cache both your data and your frontend HTML output. Set them both to the default of 1 hour. You can also set one to a specific time and the other to None. Go ahead and tweak these settings to your own requirements. I'm leaving both set to the default of 1 hour. Click on the Update button to save your caching options settings. You are now caching your View. Save your View by clicking on the Save button. The next time you look at your View interface you should see the caching time notation listed under your Basic settings. It will say 1 hour/1 hour for this setting. Once you enable Views caching, if you make a change to your View settings and configuration, the results and output of the View may not update while you have caching enabled. So, while in Views development you may want to disable caching and set it to None. Otherwise, this next section will show you how to disable your Views cache while you are in development. To see the performance results of this, you can use the Devel module's functionality again. When you load your View after you enable caching, you should see a decrease in the amount of ms (milliseconds) needed to build your Views plugin, data, and handlers. So, if your Views plugin build loaded in 27.1 ms before you enabled caching, you may notice that it changes to something less—for example, in my case it now shows that it loads in 2.8 ms. You can immediately see a slight performance increase with your View build. Let's go ahead and build a brand new View using the test content that we generated with the Devel module and then enable caching for this View as well. Go to your Views admin and follow these steps: Add a new View. Name the View, add a description and a tag if applicable. Click on Next. I'm going to create a View that filters my blog entries and lists the new blog entries in post date order using the Devel content I generated. Add a Page display to your new View. Name the page View. Give the page View a title. Give your View an HTML list style. Set the View to display 5 posts and to use a full pager. Set your caching to Time-based (following instructions above in the first view we edited). Give the view a path. Add a Node: Title field and set the field to be linked to its node. Add a filter in order to filter by Node:Type and then select Blog entry. Set your Sort criteria to sort by Node:Post date in ascending order by hour. Your settings should look similar to this: Save your View by clicking on the Save button. Your new View will be visible at the Page path you gave it and it will also be caching the content and data it presents. Again, if you refresh your View page each time you should notice that the plugins, data, and handlers build times decrease or stay very similar and consistent in load times. You should also notice that the Devel database queries status is telling you that it's using the cached results and cached output for the View build times and the MySQL statements. You should see the following code sitting below your page content on the View page you are looking at. It will resemble this: Views plugins build time: 23.509979248 msViews data build time: 55.7069778442 msViews handlers build time: 1.95503234863 msSELECT node.nid AS nid,node_data_field_photo_gallery_photo.field_photo_gallery_photo_fidAS node_data_field_photo_gallery_photo_field_photo_gallery_photo_fid,node_data_field_photo_gallery_photo.field_photo_gallery_photo_listAS node_data_field_photo_gallery_photo_field_photo_gallery_photo_list,node_data_field_photo_gallery_photo.field_photo_gallery_photo_dataAS node_data_field_photo_gallery_photo_field_photo_gallery_photo_data,node.type AS node_type,node.vid AS node_vid,node.title AS node_title,node.created AS node_createdFROM {node} nodeLEFT JOIN {content_type_photo} node_data_field_photo_gallery_photo ONnode.vid = node_data_field_photo_gallery_photo.vidWHERE (node.status <> 0) AND (node.type in ('%s'))ORDER BY node_created ASCUsed cached resultsUsed cached output
Read more
  • 0
  • 0
  • 3182

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
  • 2167

article-image-build-your-own-application-access-twitter-using-java-and-netbeans-part-2
Packt
19 Feb 2010
17 min read
Save for later

Build your own Application to access Twitter using Java and NetBeans: Part 2

Packt
19 Feb 2010
17 min read
In this tutorial, we’ll develop the simple Java application further to add some more functions. Now that we can connect to our Twitter account via the Twitter4J API, it would be nice to use a login dialog instead of hard-coding our Twitter username and password in the Java application. But before we start to build our enhanced SwingAndTweet application, let me show you how it will look like once we finish all the exercises in this part of the tutorial: And now, let the show begin… Creating a Login dialog for our SwingAndTweet application Open your NetBeans IDE along with your SwingAndTweet project, and make sure you’re in the Design View. Go to the Palette window and locate the Dialog component under the Swing Windows section; then drag and drop it anywhere inside the SwingAndTweetUI JFrame component: A JDialog will be added automatically to your SwingAndTweet application, and it will show up in the Component Inspector tab located at the lower-left part of the screen, under Other Components: Right-click on the jDialog1 component in the Inspector tab and select Change Variable Name… from the pop-up menu. The Rename dialog will show up next. Type twitterLogin in the New Name field and press Enter to change the dialog’s name. Now you can start adding text fields, labels and buttons to your twitterLogin dialog. Double-click on the twitterLogin component under the Inspector tab. The twitterLogin dialog will show up empty in the Design Editor window. Use the Palette window to add two JLabels, one JTextField, one JPasswordField and two JButtons to the twitterLogin dialog. Arrange these controls as shown below: Now let’s change the names of the JTextField control, the JPasswordField control and the two JButton controls, so we can easily identify them within your SwingAndTweet application’s code. Right-click on the first text field (jLabel2), select Change Variable Name… from the pop-up menu and replace the text field’s name with txtUsername. Do the same with the other fields; use txtPassword for the JPasswordField control, btnLogin for the Login button and btnExit for the Exit button. And now the last touch. Right-click anywhere inside the twitterLogin dialog, being careful not to right-click inside any of the controls, and select the Properties option from the pop-up menu. The twitterLogin [JDialog] – Properties dialog will appear next. Locate the title property, double-click on the null value and type Twitter Login to replace it. Next, scroll down the properties list until you find the modal property; click on its checkbox to enable it and then click on Close to save your changes. Basically, in the previous exercise we added all the Swing controls you’re going to need to type your username and password, so you can connect to your Twitter account. The twitterLogin dialog is going to take care of the login process for your SwingAndTweet application. We replaced the default names for the JTextField, the JPasswordField and the two JButton controls because it will be easier to identify them during the coding process of the application. On step 8 we used the Properties window of the twitterLogin dialog to change the title property and give your dialog a decent title. We also enabled the modal property on step 9, so you can’t just close the dialog and jump right to the SwingAndTweetUI main window; you’ll have to enter a valid Twitter username and password combination for that. Invoking the Login dialog Ok, now we have a good-looking dialog called twitterLogin. The next step is to invoke it before our main SwingAndTweet JFrame component shows up, so we need to insert some code inside the SwingAndTweetUI() constructor method. Click on the Source button of the Editor window to change to the Source View: Now locate the SwingAndTweetUI() constructor, and type the following lines right after the initComponents(); line: int loginWidth = twitterLogin.getPreferredSize().width; int loginHeight = twitterLogin.getPreferredSize().height; twitterLogin.setBounds(0,0,loginWidth,loginHeight); twitterLogin.setVisible(true); The code inside the SwingAndTweetUI() constructor method shall now look like this: To see your new twitterLogin dialog in action, press F6 or select Run | Run Project to run your SwingAndTweetUI application. The twitterLogin dialog will pop right up. You’ll be able to type in your username and password, but since we haven’t added any functionality yet, the buttons won’t do anything right now. Click on the Close (X) button to close the dialog window and the SwingAndTweetUI main window will appear next. Click on its Close (X) button to exit your Twitter Java application. Now let’s take a look at the code we added to your twitterLogin dialog. On the first line, int loginWidth = twitterLogin.getPreferredSize().width; we declare an integer variable named loginWidth, and assign to it the preferred width of the twitterLogin dialog. The getPreferredSize method retrieves the value of the preferredSize property from the twitterLogin dialog through the .width field. On the second line, int loginHeight = twitterLogin.getPreferredSize().height; we declare another integer variable named loginHeight, and assign to it the preferred height of the twitterLogin dialog. This time, the getPreferredSize() method retrieves the value of the preferredWidth property from the twitterLogin dialog through the .height field. On the next line, twitterLogin.setBounds(0,0,loginWidth,loginHeight); we use the setBounds method to set the x,y coordinates where the dialog should appear on the screen, along with its corresponding width and height. The first two parameters are for the x,y coordinates; in this case, x=0 and y=0, which means the twitterLogin dialog will show up at the upper-left part of the screen. The last two parameters receive the value of the loginWidth and loginHeight variables to establish the twitterLogin dialog’s width and height, respectively. The last line, twitterLogin.setVisible(true); makes the twitterLogin dialog appear on the screen. And since the modal property of this dialog is enabled, once it shows up on the screen it won’t let you do anything else with your SwingAndTweet1 application until you close it up or enter a valid Twitter username and password, as we’ll see in the next exercise. Adding functionality to the twitterLogin dialog Now your twitterLogin dialog is ready to roll! Basically, it won’t let you go to the SwingAndTweet main window until you’ve entered a valid Twitter username and password. And for doing that, we’re going to use the same login code from Build your own Application to access Twitter using Java and NetBeans: Part 1 of this article series. Go to the end of your SwingAndTweetUI application source code and locate the // Variables declaration – do not modify line. Below this line, you’ll see all the variables used in your application: the btnExit and btnLogin buttons, the text fields from your twitterLogin dialog and your SwingAndTweetUI main window, etc. Add the following line just below the // End of variables declaration line:     Twitter twitter; Now click on the Design button to change to the Design View: You’ll see the twitterLogin dialog again –in case you don’t, double-click on the twitterLogin component under the Inspector tab. Now double-click on the Login button to go back to the Code View. The btnLoginActionPerformed method will show up next. Add the following code inside this method: try { twitter = new Twitter(txtUsername.getText(), String.valueOf(txtPassword.getPassword())); twitter.verifyCredentials(); JOptionPane.showMessageDialog(null, "You're logged in!"); twitterLogin.dispose(); } catch (TwitterException e) { JOptionPane.showMessageDialog (null, "Login failed"); } Make sure you write each line on its own, to avoid errors. The btnLoginActionPerformed method shall look like this: Now you’re ready to test your twitterLogin dialog. Press F6 to run your application. The Twitter Login dialog will show up next. Type your Twitter username and password, and then click on the OK button. If the username and password are correct, the You’re logged in! dialog will show up and you’ll be able to go to the SwingAndTweetUI main window. If they’re not correct, the Login failed dialog will appear instead and, after you click on the OK button, you’ll return to the twitterLogin dialog until you type a correct Twitter username and password combination. To exit your SwingAndTweetUI application, click on the Close(X) button of the twitterLogin dialog and then on the Close(X) button of the SwingAndTweetUI window. You’ll be taken back to the NetBeans IDE. Click on the Design button to go back to the Design View, and double-click on the Exit button to open the btnExitActionPerformed method. Type System.exit(0); inside the btnExitActionPerformed method, as shown below: Now go back to the Design View again, right-click anywhere inside the twitterLogin dialog (just be careful not to right-click over any of the dialog’s controls) and select the Events | Window | windowClosing option from the pop-up menu: NetBeans will change to Code View automatically and you’ll be inside the twitterLoginWindowClosing method. Type System.exit(0); inside this method, as shown below: Now run your application to test the new functionality in your loginTwitter dialog. You’ll be able to exit the SwingAndTweet application when clicking on the Exit or Close(X) buttons, and you’ll be able to go to your application’s main window if you type a correct Twitter username and password combination. You can close your SwingAndTweet application now. And now, let’s examine what we just accomplished. First you added the Twitter twitter; line to your application code. With this line we’re declaring a Twitter object named twitter, and it will be available throughout all the application code. On step 4, you added some lines of code to the btnLoginActionPerformed method; this code will be executed every time you click on the Login button from the twitterLogin dialog. All the code is enclosed in a try block, so that if an error occurs during the login process, a TwitterException will be thrown and the code inside the catch block will execute. The first line inside the try block is twitter = new Twitter(txtUsername.getText(),String.valueOf(txtPassword.getPassword())); This code creates the twitter object that we’re going to use throughout the application. It uses the text value you entered in the txtUsername and txtPassword fields to log into your Twitter account. The next line, twitter.verifyCredentials(); checks to see if the username and password provided to the twitter object are correct; if that’s true, a message dialog box shows up in the screen with the You’re logged in! message and the rest of the code executes once you click on the OK button of this message dialog; otherwise, the code in the catch block executes and a message dialog shows up in the screen with the Login failed message, and the twitterLogin dialog keeps waiting for you to type a correct username and password combination. The next line in the sequence, JOptionPane.showMessageDialog(null, "You're logged in!"); shows the message dialog that we talked about before, and the last line inside the try block, twitterLogin.dispose(); makes the twitterLogin dialog disappear from the screen once you’ve logged into your Twitter account successfully. The only line of code inside the catch block is JOptionPane.showMessageDialog (null, "Login failed"); This line executes when there’s an error in the Twitter login process; it shows the Login failed message in the screen and waits for you to press the OK button. On step 9 we added one line of code to the btnExitActionPerformed method: System.exit(0); This line closes your SwingAndTweet application whenever you click on the Exit button. Finally, on steps 10-12 we added another System.exit(0); line to the twitterLoginWindowClosing method, to close your SwingAndTweet application whenever you click on the Close(X) button of the twitterLogin dialog. Showing your Twitter timeline right after logging in Now let’s see some real Twitter action! The following exercise will show you how to show your most recent tweets inside a text area. Click on the Design button to go to the Design View; then double-click on the [JFrame] component under the Inspector tab to show the SwingAndTweetUI dialog in the Design View window: The SwingAndTweet frame will show the three controls we created during Build your own Application to access Twitter using Java and NetBeans: Part 1. Replace the My Last Tweet text in the JLabel control with the What’s happening text. Then right-click on the JTextField control and select the Change Variable Name… option from the pop-up menu, to change its name from jTextField1 to txtUpdateStatus. Now do the same with the JButton control. Right-click on it and select the Change Variable Name… option from the pop-up menu to change its name from jButton1 to btnUpdateStatus. Right-click on the same button again, but this time select the Edit Text option from the pop-up menu and replace the Login text with Update. Rearrange the three controls as per the following screenshot (you’ll need to make the SwingAndTweet container wider): Now drag a JTextArea control from the Palette window and drop it inside the SwingAndTweetUI container. Resize the text area so it fills up the rest of the container, as shown below: Double-click on the Update button to open the btnUpdateStatusActionPerformed method. The first thing you’ll notice is that it’s not empty; this is because this used to be the old Login button, remember? Now just replace all the code inside this method, as shown below: private void btnUpdateStatusActionPerformed(java.awt.event.ActionEvent evt) { try { if (txtUpdateStatus.getText().isEmpty()) JOptionPane.showMessageDialog (null, "You must write something!"); else { twitter.updateStatus(txtUpdateStatus.getText()); jTextArea1.setText(null); java.util.List<Status> statusList = twitter.getUserTimeline(); for (int i=0; i<statusList.size(); i++) { jTextArea1.append(String.valueOf(statusList.get(i).getText())+"n"); jTextArea1.append("-----------------------------n"); } } } catch (TwitterException e) { JOptionPane.showMessageDialog (null, "A Twitter Error ocurred!"); } txtUpdateStatus.setText(""); jTextArea1.updateUI(); The next step is to modify your btnLoginActionPerformed method; we need to add several lines of code to show your Twitter timeline. The complete method is shown below (the lines you need to add are shown in bold): private void btnLoginActionPerformed(java.awt.event.ActionEvent evt) { try { twitter = new Twitter(txtUsername.getText(), String.valueOf(txtPassword.getPassword())); twitter.verifyCredentials(); // JOptionPane.showMessageDialog(null, "You're logged in!"); java.util.List<Status> statusList = twitter.getUserTimeline(); for (int i=0; i<statusList.size(); i++) { jTextArea1.append(String.valueOf(statusList.get(i).getText())+"n"); jTextArea1.append("-----------------------------n"); } twitterLogin.dispose(); } catch (TwitterException e) { JOptionPane.showMessageDialog (null, "Login failed"); } jTextArea1.updateUI(); } Once you have added all the necessary code in each button’s actionPerformed method, press F6 to run the SwingAndTweet application and check if all things work as intended. If you type a message in the txtUpdateStatus text field and then click on the Update button, the timeline information inside the JTextArea1 control will change to reflect your new Twitter status: You can close your SwingAndTweet application now. That was cool, right? Now you have a much better-looking Twitter client! And you can update your status, too! Let’s examine the code we added in this last exercise… private void btnUpdateStatusActionPerformed(java.awt.event.ActionEvent evt) { try { if (txtUpdateStatus.getText().isEmpty()) JOptionPane.showMessageDialog (null, "You must write something!"); else { twitter.updateStatus(txtUpdateStatus.getText()); jTextArea1.setText(null); java.util.List<Status> statusList = twitter.getUserTimeline(); for (int i=0; i<statusList.size(); i++) { jTextArea1.append(String.valueOf(statusList.get(i).getText())+"n"); jTextArea1.append("-----------------------------n"); } } } catch (TwitterException e) { JOptionPane.showMessageDialog (null, "A Twitter Error ocurred!"); } txtUpdateStatus.setText(""); jTextArea1.updateUI(); On step 7 we added some code to the btnUpdateStatusActionPerformed method. This code will execute whenever you click on the Update button to update your Twitter status. First, let’s look at the code inside the try block. The first two lines, if (txtUpdateStatus.getText().isEmpty()) JOptionPane.showMessageDialog (null, "You must write something!"); are the first part of a simple if-else statement that checks to see if you’ve written something inside the txtUpdateStatus text field; if it’s empty, a message dialog will show the You must write something! message on the screen, and then it will wait for you to click on the OK button. If the txtUpdateStatus text field is not empty, the code inside the else block will execute instead of showing up the message dialog. The next part of the code is the else block. The first line inside this block, twitter.updateStatus(txtUpdateStatus.getText()); updates your twitter status with the text you wrote in the txtUpdateStatus text field; if an error occurs at this point, a TwitterException is thrown and the program execution will jump to the catch block. If your Twitter status was updated correctly, the next line to execute is jTextArea1.setText(null); This line erases all the information inside the jTextArea1 control. And the next line, java.util.List<Status> statusList = twitter.getUserTimeline(); gets the 20 most recent tweets from your timeline and assigns them to the statusList variable. The next line is the beginning of a for statement: for (int i=0; i<statusList.size(); i++) { Basically, what this for block does is iterate through all the 20 most recent tweets in your timeline, one at a time, executing the two statements inside this block on each iteration: jTextArea1.append(String.valueOf(statusList.get(i).getText())+"n"); jTextArea1.append("-----------------------------n"); Although the getUserTimeline() function retrieves the 20 most recent tweets, we need to use the statusList.size() statement as the loop continuation condition inside the for block to get the real number of tweets obtained, because they can be less than 20, and we can’t iterate through something that maybe doesn’t exist, right? The first line appends the text of each individual tweet to the jTextArea1 control, along with a new-line character ("n") so each tweet is shown in one individual line, and the second line appends the "-----------------------------n" text as a separator between each individual tweet, along with a new-line character. The final result is a list of the 20 most recent tweets inside the jTextArea1 control. The only line of code inside the catch block displays the A Twitter Error occurred! message in case something goes wrong when trying to update your Twitter status. The next line of code right after the catch block is txtUpdateStatus.setText(""); This line just clears the content inside the txtUpdateStatus control, so you don’t accidentally insert the same message two times in a row. And finally, the last line of code in the btnUpdateStatusActionPerformed method is jTextArea1.updateUI(); This line updates the jTextArea1 control, so you can see the list of your 20 most recent tweets after updating your status. private void btnLoginActionPerformed(java.awt.event.ActionEvent evt) { try { twitter = new Twitter(txtUsername.getText(), String.valueOf(txtPassword.getPassword())); twitter.verifyCredentials(); // JOptionPane.showMessageDialog(null, "You're logged in!"); java.util.List<Status> statusList = twitter.getUserTimeline(); for (int i=0; i<statusList.size(); i++) { jTextArea1.append(String.valueOf(statusList.get(i).getText())+"n"); jTextArea1.append("-----------------------------n"); } twitterLogin.dispose(); } catch (TwitterException e) { JOptionPane.showMessageDialog (null, "Login failed"); } jTextArea1.updateUI(); And now let’s have a look at the code we added inside the btnLoginActionPerformed method. The first thing you’ll notice is that we’ve added the '//' characters to the // JOptionPane.showMessageDialog(null, "You're logged in!"); line; this means it’s commented out and it won’t be executed, because it’s safe to go directly to the SwingAndTweet main window right after typing your Twitter username and password. The next lines are identical to the ones inside the btnUpdateStatusActionPerformed method we saw before; the first line retrieves your 20 most recent tweets, and the for block displays the list of tweets inside the jTextArea1 control.  And the last line of code, jTextArea1.updateUI(); updates the jTextArea1 control so you can see the most recent information regarding your latest tweets. Summary Well, now your SwingAndTweet application looks better, don’t you think so? In this article, we enhanced the SwingAndTweet application which we build in the first part of the tutorials series. In short, we: Created a twitterLogin dialog to take care of the login process Added functionality to show your 20 most recent tweets right after logging in Added the functionality to update your Twitter status
Read more
  • 0
  • 0
  • 4291

article-image-ajax-form-validation-part-2
Packt
19 Feb 2010
11 min read
Save for later

AJAX Form Validation: Part 2

Packt
19 Feb 2010
11 min read
How to implement AJAX form validation In this article, we redesigned the code for making AJAX requests when creating the XmlHttp class. The AJAX form validation application makes use of these techniques. The application contains three pages: One page renders the form to be validated Another page validates the input The third page is displayed if the validation is successful The application will have a standard structure, composed of these files: index.php: It is the file loaded initially by the user. It contains references to the necessary JavaScript files and makes asynchronous requests for validation to validate.php. index_top.php: It is a helper file loaded by index.php and contains several objects for rendering the HTML form. validate.css: It is the file containing the CSS styles for the application. json2.js: It is the JavaScript file used for handling JSON objects. xhr.js: It is the JavaScript file that contains our XmlHttp object used for making AJAX requests. validate.js: It is the JavaScript file loaded together with index.php on the client side. It makes asynchronous requests to a PHP script called validate.php to perform the AJAX validation. validate.php: It is a PHP script residing on the same server as index.php, and it offers the server-side functionality requested asynchronously by the JavaScript code in index.php. validate.class.php: It is a PHP script that contains a class called Validate, which contains the business logic and database operations to support the functionality of validate.php. config.php: It will be used to store global configuration options for your application, such as database connection data, and so on. error_handler.php: It contains the error-handling mechanism that changes the text of an error message into a human-readable format. allok.php: It is the page to be displayed if the validation is successful. Time for action – AJAX form validation Connect to the ajax database and create a table named users with the following code: CREATE TABLE users ( user_id INT UNSIGNED NOT NULL AUTO_INCREMENT, user_name VARCHAR(32) NOT NULL, PRIMARY KEY (user_id) ); Execute the following INSERT commands to populate your users table with some sample data: INSERT INTO users (user_name) VALUES ('bogdan'); INSERT INTO users (user_name) VALUES ('audra'); INSERT INTO users (user_name) VALUES ('cristian'); Let's start writing the code with the presentation tier. We'll define the styles for our form by creating a file named validate.css, and adding the following code to it: body { font-family: Arial, Helvetica, sans-serif; font-size: 0.8em; color: #000000; } label { float: left; width: 150px; font-weight: bold; } input, select { margin-bottom: 3px; } .button { font-size: 2em; } .left { margin-left: 150px; } .txtFormLegend { color: #777777; font-weight: bold; font-size: large; } .txtSmall { color: #999999; font-size: smaller; } .hidden { display: none; } .error { display: block; margin-left: 150px; color: #ff0000; } Now create a new file named index_top.php, and add the following code to it. This script will be loaded from the main page index.php. <?php // enable PHP session session_start(); // Build HTML <option> tags function buildOptions($options, $selectedOption) { foreach ($options as $value => $text) { if ($value == $selectedOption) { echo '<option value="' . $value . '" selected="selected">' . $text . '</option>'; } else { echo '<option value="' . $value . '">' . $text . '</option>'; } } } // initialize gender options array $genderOptions = array("0" => "[Select]", "1" => "Male", "2" => "Female"); // initialize month options array $monthOptions = array("0" => "[Select]", "1" => "January", "2" => "February", "3" => "March", "4" => "April", "5" => "May", "6" => "June", "7" => "July", "8" => "August", "9" => "September", "10" => "October", "11" => "November", "12" => "December"); // initialize some session variables to prevent PHP throwing // Notices if (!isset($_SESSION['values'])) { $_SESSION['values']['txtUsername'] = ''; $_SESSION['values']['txtName'] = ''; $_SESSION['values']['selGender'] = ''; $_SESSION['values']['selBthMonth'] = ''; $_SESSION['values']['txtBthDay'] = ''; $_SESSION['values']['txtBthYear'] = ''; $_SESSION['values']['txtEmail'] = ''; $_SESSION['values']['txtPhone'] = ''; $_SESSION['values']['chkReadTerms'] = ''; } if (!isset($_SESSION['errors'])) { $_SESSION['errors']['txtUsername'] = 'hidden'; $_SESSION['errors']['txtName'] = 'hidden'; $_SESSION['errors']['selGender'] = 'hidden'; $_SESSION['errors']['selBthMonth'] = 'hidden'; $_SESSION['errors']['txtBthDay'] = 'hidden'; $_SESSION['errors']['txtBthYear'] = 'hidden'; $_SESSION['errors']['txtEmail'] = 'hidden'; $_SESSION['errors']['txtPhone'] = 'hidden'; $_SESSION['errors']['chkReadTerms'] = 'hidden'; } ?> Now create index.php, and add the following code to it: <?php require_once ('index_top.php'); ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www. w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html > <head> <title>Degradable AJAX Form Validation with PHP and MySQL</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <link href="validate.css" rel="stylesheet" type="text/css" /> </head> <body onload="setFocus();"> <script type="text/javascript" src="json2.js"></script> <script type="text/javascript" src="xhr.js"></script> <script type="text/javascript" src="validate.js"></script> <fieldset> <legend class="txtFormLegend"> New User Registratio Form </legend> <br /> <form name="frmRegistration" method="post" action="validate.php"> <input type="hidden" name="validationType" value="php"/> <!-- Username --> <label for="txtUsername">Desired username:</label> <input id="txtUsername" name="txtUsername" type="text" onblur="validate(this.value, this.id)" value="<?php echo $_SESSION['values'] ['txtUsername'] ?>" /> <span id="txtUsernameFailed" class="<?php echo $_SESSION['errors']['txtUsername'] ?>"> This username is in use, or empty username field. </span> <br /> <!-- Name --> <label for="txtName">Your name:</label> <input id="txtName" name="txtName" type="text" onblur="validate(this.value, this.id)" value="<?php echo $_SESSION['values']['txtName'] ?>" /> <span id="txtNameFailed" class="<?php echo $_SESSION['errors']['txtName']?>"> Please enter your name. </span> <br /> <!-- Gender --> <label for="selGender">Gender:</label> <select name="selGender" id="selGender" onblur="validate(this.value, this.id)"> <?php buildOptions($genderOptions, $_SESSION['values']['selGender']); ?> </select> <span id="selGenderFailed" class="<?php echo $_SESSION['errors']['selGender'] ?>"> Please select your gender. </span> <br /> <!-- Birthday --> <label for="selBthMonth">Birthday:</label> <!-- Month --> <select name="selBthMonth" id="selBthMonth" onblur="validate(this.value, this.id)"> <?php buildOptions($monthOptions, $_SESSION['values']['selBthMonth']); ?> </select> &nbsp;-&nbsp; <!-- Day --> <input type="text" name="txtBthDay" id="txtBthDay" maxlength="2" size="2" onblur="validate(this.value, this.id)" value="<?php echo $_SESSION['values']['txtBthDay'] ?>" /> &nbsp;-&nbsp; <!-- Year --> <input type="text" name="txtBthYear" id="txtBthYear" maxlength="4" size="2" onblur="validate(document.getElementById ('selBthMonth').options[document.getElementById ('selBthMonth').selectedIndex].value + '#' + document.getElementById('txtBthDay').value + '#' + this.value, this.id)" value="<?php echo $_SESSION['values']['txtBthYear'] ?>" /> <!-- Month, Day, Year validation --> <span id="selBthMonthFailed" class="<?php echo $_SESSION['errors']['selBthMonth'] ?>"> Please select your birth month. </span> <span id="txtBthDayFailed" class="<?php echo $_SESSION['errors']['txtBthDay'] ?>"> Please enter your birth day. </span> <span id="txtBthYearFailed" class="<?php echo $_SESSION['errors']['txtBthYear'] ?>"> Please enter a valid date. </span> <br /> <!-- Email --> <label for="txtEmail">E-mail:</label> <input id="txtEmail" name="txtEmail" type="text" onblur="validate(this.value, this.id)" value="<?php echo $_SESSION['values']['txtEmail'] ?>" /> <span id="txtEmailFailed" class="<?php echo $_SESSION['errors']['txtEmail'] ?>"> Invalid e-mail address. </span> <br /> <!-- Phone number --> <label for="txtPhone">Phone number:</label> <input id="txtPhone" name="txtPhone" type="text" onblur="validate(this.value, this.id)" value="<?php echo $_SESSION['values']['txtPhone'] ?>" /> <span id="txtPhoneFailed" class="<?php echo $_SESSION['errors']['txtPhone'] ?>"> Please insert a valid US phone number (xxx-xxx-xxxx). </span> <br /> <!-- Read terms checkbox --> <input type="checkbox" id="chkReadTerms" name="chkReadTerms" class="left" onblur="validate(this.checked, this.id)" <?php if ($_SESSION['values']['chkReadTerms'] == 'on') echo 'checked="checked"' ?> /> I've read the Terms of Use <span id="chkReadTermsFailed" class="<?php echo$_SESSION['errors'] ['chkReadTerms'] ?>"> Please make sure you read the Terms of Use. </span> <!-- End of form --> <hr /> <span class="txtSmall">Note: All fields arerequired. </span> <br /><br /> <input type="submit" name="submitbutton" value="Register" class="left button" /> </form> </fieldset> </body> </html> Create a new file named allok.php, and add the following code to it: <?php // clear any data saved in the session session_start(); session_destroy(); ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html > <head> <title>AJAX Form Validation</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <link href="validate.css" rel="stylesheet" type="text/css" /> </head> <body> Registration Successful!<br /> <a href="index.php" title="Go back">&lt;&lt; Go back</a> </body> </html> Copy json2.js (which you downloaded in a previous exercise from http://json.org/json2.js) to your ajax/validate folder. Create a file named validate.js. This file performs the client-side functionality, including the AJAX requests: // holds the remote server address var serverAddress = "validate.php"; // when set to true, display detailed error messages var showErrors = true; // the function handles the validation for any form field function validate(inputValue, fieldID) { // the data to be sent to the server through POST var data = "validationType=ajax&inputValue=" + inputValue + "&fieldID=" + fieldID; // build the settings object for the XmlHttp object var settings = { url: serverAddress, type: "POST", async: true, complete: function (xhr, response, status) { if (xhr.responseText.indexOf("ERRNO") >= 0 || xhr.responseText.indexOf("error:") >= 0 || xhr.responseText.length == 0) { alert(xhr.responseText.length == 0 ? "Server error." : response); } result = response.result; fieldID = response.fieldid; // find the HTML element that displays the error message = document.getElementById(fieldID + "Failed"); // show the error or hide the error message.className = (result == "0") ? "error" : "hidden"; }, data: data, showErrors: showErrors }; // make a server request to validate the input data var xmlHttp = new XmlHttp(settings); } // sets focus on the first field of the form function setFocus() { document.getElementById("txtUsername").focus(); } Now it's time to add the server-side logic. Start by creating config.php, with the following code in it: <?php // defines database connection data define('DB_HOST', 'localhost'); define('DB_USER', 'ajaxuser'); define('DB_PASSWORD', 'practical'); define('DB_DATABASE', 'ajax'); ?> Now create the error handler code in a file named error_handler.php: <?php // set the user error handler method to be error_handler set_error_handler('error_handler', E_ALL); // error handler function function error_handler($errNo, $errStr, $errFile, $errLine) { // clear any output that has already been generated if(ob_get_length()) ob_clean(); // output the error message $error_message = 'ERRNO: ' . $errNo . chr(10) . 'TEXT: ' . $errStr . chr(10) . 'LOCATION: ' . $errFile . ', line ' . $errLine; echo $error_message; // prevent processing any more PHP scripts exit; } ?> The PHP script that handles the client's AJAX calls, and also handles the validation on form submit, is validate.php: <?php // start PHP session session_start(); // load error handling script and validation class require_once ('error_handler.php'); require_once ('validate.class.php'); // Create new validator object $validator = new Validate(); // read validation type (PHP or AJAX?) $validationType = ''; if (isset($_POST['validationType'])) { $validationType = $_POST['validationType']; } // AJAX validation or PHP validation? if ($validationType == 'php') { // PHP validation is performed by the ValidatePHP method, //which returns the page the visitor should be redirected to //(which is allok.php if all the data is valid, or back to //index.php if not) header('Location:' . $validator->ValidatePHP()); } else { // AJAX validation is performed by the ValidateAJAX method. //The results are used to form a JSON document that is sent //back to the client $response = array('result' => $validator->ValidateAJAX ($_POST['inputValue'],$_POST['fieldID']), 'fieldid' => $_POST['fieldID'] ); // generate the response if(ob_get_length()) ob_clean(); header('Content-Type: application/json'); echo json_encode($response); } ?>
Read more
  • 0
  • 0
  • 5871

article-image-jquery-14-dom-manipulation-methods-replacement-copying-and-removal
Packt
19 Feb 2010
5 min read
Save for later

jQuery 1.4 DOM Manipulation Methods for Replacement, Copying and Removal

Packt
19 Feb 2010
5 min read
DOM replacement These methods are used to remove content from the DOM and replace it with new content. .html() (getter) Get the HTML contents of the first element in the set of matched elements. .html() Parameters None Return value A string containing the HTML representation of the element. Description This method is not available on XML documents. In an HTML document, we can use .html() to get the contents of any element. If our selector expression matches more than one element, only the first one's HTML content is returned. Consider the following code: $('div.demo-container').html(); In order for the content of the following <div> to be retrieved, it would have to be the first one in the document. <div class="demo-container"> <div class="demo-box">Demonstration Box</div></div> The result would look like this: <div class="demo-box">Demonstration Box</div> .html() (setter) Set the HTML contents of each element in the set of matched elements. .html(htmlString).html(function) Parameters (first version) htmlString: A string of HTML to set as the content of each matched element Parameters (second version) function: A function returning the HTML content to set Return value The jQuery object, for chaining purposes. Description The .html() method is not available in XML documents. When we use .html() to set the content of elements, any content that was in those elements is completely replaced by the new content. Consider the following HTML code: <div class="demo-container"> <div class="demo-box">Demonstration Box</div></div> We can set the HTML contents of <div class="demo-conta iner"> as follows: $('div.demo-container').html('<p>All new content. <em>You bet!</em></p>'); That line of code will replace everything inside <div class="demo-container">. <div class="demo-container"> <p>All new content. <em>You bet!</em></p></div> As of jQuery 1.4, the .html() method allows us to set the HTML content by passing in a function. $('div.demo-container').html(function() { var emph = '<em>' + $('p').length + ' paragraphs!</em>'; return '<p>All new content for ' + emph + '</p>';}); Given a document with six paragraphs, this example will set the HTML of <div class="demo-container"> to <p>All new content for <em>6 paragraphs!</em></p>. .text() (getter) Get the combined text contents of each element in the set of matched elements, including their descendants. .text() Parameters None Return value A string containing the combined text contents of the matched elements. Description Unlike the .html() method, .text() can be used in both XML and HTML documents. The result of the .text() method is a string containing the combined text of all matched elements. Consider the following HTML code: <div class="demo-container"> <div class="demo-box">Demonstration Box</div> <ul> <li>list item 1</li> <li>list <strong>item</strong> 2</li> </ul></div> The code $('div.demo-container').text() would produce the following result: Demonstration Box list item 1 list item 2 .text() (setter) Set the content of each element in the set of matched elements to the specified text. .text(textString).text(function) Parameters (first version) textString: A string of text to set as the content of each matched element Parameters (second version) function: A function returning the text to set as the content Return value The jQuery object, for chaining purposes. Description Unlike the .html() method, .text() can be used in both XML and HTML documents. We need to be aware that this method escapes the string provided as necessary so that it will render correctly in HTML. To do so, it calls the DOM method .createTextNode(), which replaces special characters with their HTML entity equivalents (such as &lt; for <). Consider the following HTML code: <div class="demo-container"> <div class="demo-box">Demonstration Box</div> <ul> <li>list item 1</li> <li>list <strong>item</strong> 2</li> </ul></div> The code $('div.demo-container').text('<p>This is a test.</p>'); will produce the following DOM output: <div class="demo-container"> &lt;p&gt;This is a test.&lt;/p&gt;</div> It will appear on a rendered page as though the tags were exposed as follows: <p>This is a test</p> As of jQuery 1.4, the .text() method allows us to set the text content by passing in a function. $('ul li').text(function() {return 'item number ' + ($(this).index() + 1);}); Given an unordered list with three <li> elements, this example will produce the following DOM output: <ul><li>item number 1</li><li>item number 2</li><li>item number 3</li></ul>
Read more
  • 0
  • 0
  • 2063

article-image-working-data-application-components-sql-server-2008-r2
Packt
19 Feb 2010
4 min read
Save for later

Working with Data Application Components in SQL Server 2008 R2

Packt
19 Feb 2010
4 min read
(For more resources on Microsoft, see here.) A Data Application Component is an entity that integrates all data tier related objects used in authoring, deploying and managing into a single unit instead of working with them separately. Programmatically DACs belong to classes that are found in The Microsoft.SqlServer.Management.Dac namespace. DACs are stored in a DacStore and managed centrally. Dacs can be authored and built using SQL Server Data-Tier Application templates in VS2010 (now in Beta 2) or using SQL Server Management Studio. This article describes creating DAC using SQL Server 2008 R2 Nov-CTP(R2 server in this article), a new feature in this version. Overview of the article In order to proceed working with this example you need to download SQL Server 2008 R2 Nov-CTP. The ease with which this installs depend on the Windows OS on your machine. I had encountered problems installing it on my Windows XP SP3 where only partial files were installed. On Windows 7 Ultimate it installed very easily. This article uses the R2 Server installed on Windows 7 Ultimate. You can download the R2 Server from this link after registering at the site. Download the x 32 version, a 1.2 GB file. In order to work with Data Tier Applications in Visual Studio you need to install Visual Studio 2010 now in Beta 2. If you have installed Beta 1, take it out (use Add/Remove programs) before you install Beta 2. You would create a Database Project as shown in the next figure. In the following sections we will look at how to extract a DAC from a existing Database using tools in SSMS and R2 Server. This will be followed by deploying the DAC to a SQL Server 2008 (before R2 Version). In a future article we will see how to create and work with the DACs in Visual Studio. Extracting a DAC We will use the Extract a Data-Tier Application wizard to create a DAC file. Connect to the SQL Server 2008 Server in the Management Studio as shown. We will create a DAC package that will create a DAC file for us on completing this task. Right click the Pubs database and click on Tasks | Extract Data-Tier Appplication... You may also use any other database for working with this exercise. This brings up the Wizard as shown in the next figure. Read the notes on this window and review the database icons on this window. Click Next. The Set Properties page of the wizard gets displayed. The Application name will be the database name with which you started. You can change it if you like. The package file name will reflect the application name. The version is set at 1.0.0.0., but you may specify any version you like. You can create different DACs with different version numbers. Click Next. The program cranks up and after validation the Validation & Summary page gets displayed as shown. The file path of the package, the name of the package and the DAC objects that got into the package are all shown here. All database objects (Tables, Views, Stored Procedures etc) are included in the package. Click Save Report button to save the packaging info to a file. This saves the HTML file ExtractDACSummary_HODENTEK3_ pubs_20100125 to the SQL Server Management Studio folder. This report shows what objects were validated during this process as shown. Click Next. The Build the Package opens up and after the build process is completed you will be able to save the package as shown in the next picture. At the package location shown earlier you will see a package object as shown. This file can be Unpacked to a destination as well as opened with Microsoft SQL Server DAC Package File Unpack wizard. These actions can be accessed by making a right click on this package file.
Read more
  • 0
  • 0
  • 2686
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 €18.99/month. Cancel anytime
article-image-jboss-richfaces-33-supplemental-installation
Packt
19 Feb 2010
6 min read
Save for later

JBoss RichFaces 3.3 Supplemental Installation

Packt
19 Feb 2010
6 min read
This installation guide is for the Windows platform. JBoss Server Installation In order to run any web application, an application server is needed. JBoss server is an industry standard and is ideal for running Seam and RichFaces applications.  Downloading the server is a very simple task. First go to the JBoss download page and download the 4.2.2.GA version of the JBoss server. Save it to a directory for downloads such as c:downloads. Unzip the file to the c: directory.  After unzipping, you should have a folder named c: jboss-4.2.2.GA. Test the server installation by going into c:jboss-4.2.2.GAbin and running the file run.bat. A command window should run with server logs. At completion, the logs will indicate that the server has started. Starting JBoss Server Within Eclipse Although you can start JBoss server from the run.bat file, for the purposes of development and learning RichFaces, it is more valuable to start the JBoss server within Eclipse. The Eclipse IDE provides support to run, shutdown and adjust settings for servers. In Eclipse, we will work from the Java Perspective. Change Eclipse to be in the Java Perspective. Go to Window->Open Perspective->Java. We need to have a tab for Server. Go To Window->Show View->Other->Servers->Server. In the Server tab, right click and go to New->Server. Here we are defining a server to launch within Eclipse. Choose JBoss->JBoss v4.2->Next. Choose the JRE, which will typically be the path to where Java is installed. For the Application Server Directory, choose c:jboss-4.2.2.GA then Next. Accept the defaults for Address, Port, JNDI Port and Server Configuration. Click Next->Finish. Next the server settings need to be adjusted. Double click on the JBOSS 4.2 entry in the server tab to bring up the settings menu in eclipse. Click on the edit menu on the right hand side. Figure 1 - JBoss Server settings Uncheck all check boxes and Server Timeout Delay to be Unlimited. The server is ready to run. In the Server tab, right click on the JBOSS 4.2 entry and choose Start. Go to the Console tab and you will see the server logs. At completion, the logs should indicate that the server has started. MySql Installation MySql is the database used to store information in the example applications. Once MySql is installed, the example applications can connect to a persistent store and the developer will be able to see data saved as the application is exercised. Go to the MySql download page and retrieve the installation file. Look for MSI file labeled mysql-essential-5.1.42-winx64.msi (or a similar version). The MSI file is easiest to install as it gives a wizard to guide you through the process. Once the file is saved, double click on it to initiate installation. Choose all the default options. When the id and password is requested, choose root as both the id and password. This is easy to remember for development purposes. Verify installation of MySql by looking for the shortcuts placed Windows Programs Menu. Also verify that MySql has been installed as a Windows service. The easiest way to do this is to go to Start-> Run in Windows and type services.msc. The services dialogue box should have a MySql entry. Make sure the MySql service is started. Run MySql Command Client In order to operate the MySql database, you can use the provided command line client. The client enables the user to look up tables, execute operational commands, and run sql statements.  In the Windows Start menu, go to Start->Programs0->MySql->MySql Server 5.x->MySql Command Line Client. Type in root for the password. A mysql prompt will appear. The command line tool is used for creating the database for the example applications. In order to import a sql script, use the command source <path>. For example: source c:adv_contact_manager_create.sql For a full list of commands for MySql, see the online manual: http://dev.mysql.com/doc/refman/5.1/en/index.html. Download and Install MySql JDBC Connector In order for Java applications to connect to MySql through JDBC, a connector jar is needed. MySql provides connectivity for client applications developed in the Java programming language via a JDBC driver, which is called MySql Connector/J. Go to the connector download page and retrieve the zip file. Unzip the file to a directory. Identify the file mysql-connector-java-5.1.10-bin.jar. Copy this file to the default server lib directory so that it is accessible by all applications: C:jboss-4.2.2.GAserverdefaultlib Build and Deploy Example Applications In order to see the application that is being developed, it is necessary to build and deploy the application onto the server. Applications generated by the seam-gen tool come built with a script armed with many build tasks. Eclipse provides Ant support so we can use it to operate the build file provided within the example application. In Eclipse with the application loaded as a project, open the Ant view. Go to Window-> Show View-> Ant. The Ant view will be displayed. Now load the build.xml in order to operate the Ant targets. Right click in the Ant view and select Add Buildfiles->Choose build.xml for the application. A list of Ant targets will be loaded. In order to execute a task, simply double click on the task. The Console window will display the executed statements. Seam-gen offers several tasks, but a notable few are very useful. deploy – builds and deploys the application to the server undeploy – deletes the application from the server purge – deletes temporary server files associated with the application clean – deletes package application files from the local distribution directory If the deploy task fails, simply go to the JBoss deployment directory and delete the installed application. C:jboss-4.2.2.GAserverdefaultdeploy Applications can also be directly copied into this directory for deployment. Inversely, applications can be directly deleted from this directory for un-deployment. With these basics installations complete, running the example applications should be simple and you will be on your way to mastering RichFaces 3.3. Summary In this article, we discussed the following: JBoss Server Installation Starting JBoss Server within Eclipse MySql Installation Build and Deploy Example applications
Read more
  • 0
  • 0
  • 2066

article-image-spring-mvc-configuring-and-deploying-application
Packt
19 Feb 2010
5 min read
Save for later

Spring MVC - Configuring and Deploying the Application

Packt
19 Feb 2010
5 min read
The first section will focus on configuring the application and its components so that the application can be deployed. The focus of the second section will be a real world application that will be developed using the steps described in the article on Developing the MVC components and in this article. That sets the agenda for this discussion. Using Spring MVC – Configuring the Application There are four main steps in configuring of the application. They are: Configure the DispatcherServlet Configure the Controller Configure the View Configure the Build Script The first step will be same for any application that is built using Spring MVC. The other three steps change according to the components that have been developed for the application. Here are the details. Configure the DispatcherServlet The first step is to tell the Application server that all the requests for this (Spring MVC based) application need to be routed to Spring MVC. This is done by setting up the DispatcherServlet. The reason for setting up DispatcherServlet is that it acts as the entry point to the Spring MVC and thus to the application.  Since the DispatcherServlet interacts with the application as a whole (instead of individual components), its configuration or setting up at application level. And any setup that needs to be done at the application level is done by making the required entries in the web.xml. The entries required  in the web.xml can be divided into the following: Servlet mapping URL mapping The former specifies the details of the servlet and the latter specifies how the servlet is related to a URL. Here are the details. Servlet mapping Servlet mapping is akin to declaring a variable. It is through servlet mapping that Application Server knows which servlets of the application it needs to support.  Servlet mapping, in essence, assigns a name to a servlet class that can be reference throughout web.xml. To set up the DispatcherServlet, first it has to be mapped to a name. that can be done using <servlet-name> and <servlet-class> tags that are the child nodes of the <servlet> tag. The following statement maps the DispatcherServlet to the name "dispatcher". <servlet> <servlet-name> dispatcher </servlet-name> <servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class> </servlet> Since the DispatcherServlet needs to be loaded on the startup of the Application Server instead of the loading when a request arrives, the optional node <load-on-startup> with value of 1 is also required. The modified <servlet> tag will be: <servlet> <servlet-name> dispatcher </servlet-name> <servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class> <load-on-startup>1</load-on-startup> </servlet> Next step is to map the URL to the servlet name so that the requests can be routed to the DispatcherServlet. URL mapping Once the servlet has been mapped, the next step is to map the servlet name with a URL so that the requests for that particular URL can be passed on to the application via the DispatcherServlet. That can be done using the <servlet-name> and <url-pattern> nodes of the <servlet-mapping> node. The <servlet-name> is used to refer the name that was mapped with the DispatcherServlet class. The <url-pattern> is used to map a URL pattern with a servlet name so that when a request arrives matching the URL pattern, Application Server can redirect it to the mapped servlet. To map the DispatcherServlet with a URL pattern the <servlet-mapping> tag will be: <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>*.html</url-pattern> </servlet-mapping> With this configuration/setting up of DispatcherServlet is complete. One point to keep in mind is that the URL pattern can be any pattern of one’s choice. However, it’s a common practice to use *.html for DispatcherServlet and *do for ActionServlet (Struts 1.x). Next step is to configure the View and Controller components of the application. Mapping the Controller By setting up the DispatcherServlet, the routing of requests to the application will be taken care of by the Application Server. However, unless the individual controllers of the application are setup/configured, the Framework would not know which controller to be called once the DispatcherServlet receives the request. The configuration of the Controller as well as the View components is done in the Spring MVC configuration file. The name of the configuration file is dependent on the name of the DispatcherServlet in web.xml, which is of the form <DispatcherServlet_name-servlet>.xml. So if the DispacherServlet is mapped to the name dispatcher, then the name of the configuration file will be dispatcher-servlet.xml. The file will reside in WEB-INF folder of the application. Everything in Spring Framework is a bean. Controllers are no exceptions. Controllers are configured as beans using the <bean> child tag of <beans> tag. A Controller is mapped by providing the URL of the request as the name attribute and complete qualified name of the Controller class as the value of the class attribute. For example, if the request URL is say, http://localhost/test/hello.html, then the name attribute will have /hello.html and the value attribute will have the fully qualified class name say, org.me.HelloWorldController. The following statements depicts the same: <bean name="/hello.html" class=" org.me.HelloWorldController "/> One point to keep in mind is that the "/" in the bean name represents the relative path. In other words, /hello.html means that hello.html is directly under http://localhost/test. If hello.html was under another directory say, jsp which, in turn was directly under the application, then the name attribute will be /jsp/hello.html. Let us move onto configuring the Views.
Read more
  • 0
  • 0
  • 3432

article-image-ajax-form-validation-part-1
Packt
18 Feb 2010
4 min read
Save for later

AJAX Form Validation: Part 1

Packt
18 Feb 2010
4 min read
The server is the last line of defense against invalid data, so even if you implement client-side validation, server-side validation is mandatory. The JavaScript code that runs on the client can be disabled permanently from the browser's settings and/or it can be easily modified or bypassed. Implementing AJAX form validation The form validation application we will build in this article validates the form at the server side on the classic form submit, implementing AJAX validation while the user navigates through the form. The final validation is performed at the server, as shown in Figure 5-1: Doing a final server-side validation when the form is submitted should never be considered optional. If someone disables JavaScript in the browser settings, AJAX validation on the client side clearly won't work, exposing sensitive data, and thereby allowing an evil-intentioned visitor to harm important data on the server (for example, through SQL injection). Always validate user input on the server. As shown in the preceding figure, the application you are about to build validates a registration form using both AJAX validation (client side) and typical server-side validation: AJAX-style (client side): It happens when each form field loses focus (onblur). The field's value is immediately sent to and evaluated by the server, which then returns a result (0 for failure, 1 for success). If validation fails, an error message will appear and notify the user about the failed validation, as shown in Figure 5-3. PHP-style (server side): This is the usual validation you would do on the server—checking user input against certain rules after the entire form is submitted. If no errors are found and the input data is valid, the browser is redirected to a success page, as shown in Figure 5-4. If validation fails, however, the user is sent back to the form page with the invalid fields highlighted, as shown in Figure 5-3. Both AJAX validation and PHP validation check the entered data against our application's rules: Username must not already exist in the database Name field cannot be empty A gender must be selected Month of birth must be selected Birthday must be a valid date (between 1-31) Year of birth must be a valid year (between 1900-2000) The date must exist in the number of days for each month (that is, there's no February 31) E-mail address must be written in a valid email format Phone number must be written in standard US form: xxx-xxx-xxxx The I've read the Terms of Use checkbox must be selected Watch the application in action in the following screenshots: XMLHttpRequest, version 2 We do our best to combine theory and practice, before moving on to implementing the AJAX form validation script, we'll have another quick look at our favorite AJAX object—XMLHttpRequest. On this occasion, we will step up the complexity (and functionality) a bit and use everything we have learned until now. We will continue to build on what has come before as we move on; so again, it's important that you take the time to be sure you've understood what we are doing here. Time spent on digging into the materials really pays off when you begin to build your own application in the real world. Our OOP JavaScript skills will be put to work improving the existing script that used to make AJAX requests. In addition to the design that we've already discussed, we're creating the following features as well: Flexible design so that the object can be easily extended for future needs and purposes The ability to set all the required properties via a JSON object We'll package this improved XMLHttpRequest functionality in a class named XmlHttp that we'll be able to use in other exercises as well. You can see the class diagram in the following screenshot, along with the diagrams of two helper classes: settings is the class we use to create the call settings; we supply an instance of this class as a parameter to the constructor of XmlHttp complete is a callback delegate, pointing to the function we want executed when the call completes The final purpose of this exercise is to create a class named XmlHttp that we can easily use in other projects to perform AJAX calls. With our goals in mind, let's get to it! Time for action – the XmlHttp object In the ajax folder, create a folder named validate, which will host the exercises in this article.
Read more
  • 0
  • 0
  • 6943

article-image-web-scraping-python
Packt
17 Feb 2010
5 min read
Save for later

Web Scraping with Python

Packt
17 Feb 2010
5 min read
To perform this task, usually three basic steps are followed: Explore the website to find out where the desired information is located in the HTML DOM tree Download as many web pages as needed Parse downloaded web pages and extract the information from the places found in the exploration step The exploration step is performed manually with the aid of some tools that make it easier to locate the information and reduce the development time in next steps. The download and parsing steps are usually performed in an iterative cycle since they are interrelated. This is because the next page to download may depend on a link or similar in the current page, so not every web page can be downloaded without previously looking into the earlier one. This article will show an example covering the three steps mentioned and how this could be done using python with some development. The code that will be displayed is guaranteed to work at the time of writing, however it should be taken into account that it may stop working in future if the presentation format changes. The reason is that web scraping depends on the DOM tree to be stable enough, that is to say, as happens with regular expressions, it will work fine for slight changes in the information being parsed. However, when the presentation format is completely changed, the web scraping scripts have to be modified to match the new DOM tree. Explore Let's say you are a fan of Pack Publishing article network and that you want to keep a list of the titles of all the articles that have been published until now and the link to them. First of all, you will need to connect to the main article network page (http://www.packtpub.com/article-network) and start exploring the web page to have an idea about where the information that you want to extract is located. Many ways are available to perform this task such as view the source code directly in your browser or download it and inspect it with your favorite editor. However, HTML pages often contain auto-generated code and are not as readable as they should be, so using a specialized tool might be quite helpful. In my opinion, the best one for this task is the Firebug add-on for the Firefox browser. With this add-on, instead of looking carefully in the code looking for some string, all you have to do is press the Inspect button, move the pointer to the area in which you are interested and click. After that, the HTML code for the area marked and the location of the tag in the DOM tree will be clearly displayed. For example, the links to the different pages containing all the articles are located inside a right tag, and, in every page, the links to the articles are contained as list items in an unnumbered list. In addition to this, the links URLs, as you probably have noticed while reading other articles, start with http://www.packtpub.com/article/ So, our scraping strategy will be Get the list of links to all pages containing articles Follow all links so as to extract the article information in all pages One small optimization here is that main article network page is the same as the one pointed by the first page link, so we will take this into account to avoid loading the same page twice when we develop the code. Download Before parsing any web page, the contents of that page must be downloaded. As usual, there are many ways to do this: Creating your own HTTP requests using urllib2 standard python library Using a more advanced library that provides the capability to navigate through a website simulating a browser such as  mechanize. In this article mechanize will be covered as it is the easiest choice. mechanize is a library that provides a Browser class that lets the developer to interact with a website in a similar way a real browser would. In particular it provides methods to open pages, follow links, change form data and submit forms. Recalling the scraping strategy in our previous version, the first thing we would like to do is to download the main article network web page. To do that we will create a Browser class instance and then open the main article network page: >>> import mechanize>>> BASE_URL = "http://www.packtpub.com/article-network">>> br = mechanize.Browser()>>> data = br.open(BASE_URL).get_data()>>> links = scrape_links(BASE_URL, data) Where the result of the open method is an HTTP response object, the get_data method returns the contents of the web page. The scrape_links function will be explained later. For now, as pointed out in the introduction section, bear in mind that the downloading and parsing steps are usually performed iteratively since some contents to be downloaded depends on the parsing done in some kind of initial contents such as in this case.
Read more
  • 0
  • 0
  • 9367
article-image-author-podcast-aleksander-seovic-talks-about-oracle-coherence-35
Packt
17 Feb 2010
1 min read
Save for later

Author Podcast - Aleksander Seovic Talks About Oracle Coherence 3.5

Packt
17 Feb 2010
1 min read
Aleksander Seovic is the author of Oracle Coherence 3.5, which will help you to design and build scalable, reliable, high-performance applications using software of the same name. The book is due out in March, but you can get a flavour of it in his interview with Cameron Purdy, below. For more information on Aleksander's book, visit: http://www.packtpub.com/oracle-coherence-3-5/book. Listen Here      
Read more
  • 0
  • 0
  • 3361

article-image-trunks-using-3cx-part-1
Packt
12 Feb 2010
11 min read
Save for later

Trunks using 3CX: Part 1

Packt
12 Feb 2010
11 min read
PSTN trunks A Public Switch Telephone Network (PSTN) trunk is an old fashioned analog Basic Rate Interface (BRI) ISDN or Primary Rate Interface (PRI) phone line. 3CX can use any of these with the correct analog to SIP gateway. Usually these come into your home or business through a pair of copper lines. Depending on where you live, this may be the only means of connecting 3CX and communicating outside of your network. One of the advantages of a PSTN line is reliability and great call quality. Unless the wires break, you will almost always have phone service. However, what about call quality? After all, many people would like to have a comparison between VoIP and PSTN. Analog hardware for BRI ISDN and PRI's will be discussed in greater detail in Chapter 9. For using an analog PSTN line, you will need an FXO gateway. There are many external ones available. Until Sangoma introduced a new line at the end of 2008, there had not been any gateway which worked inside a Windows PC with 3CX. There are many manufacturers of analog gateways such as Linksys, Audio-Codes, Patton Electronics, GrandStream, and Sangoma. What these FXO gateways do is convert the analog phone line into IP signaling. Then the IP signaling gets passed over your network to the 3CX server and your phones. My personal preference is Patton Electronics. They are probably the most expensive FXOs' out there, but in this case, you get what you pay for. I have tried all of them and they all work. Some have issues with echo which can be hard to get rid of without support, or lots of trial and error, whereas some cannot support high demands (40 calls/hour) without needing to be reset every day, so if you are just testing, get a low-end one. For a high demand business, my preference is Patton. Not only do they make great products, but their support is top notch too. We will configure a Patton SmartNode SN4114 later in this article. SIP trunks What is a SIP trunk? A SIP trunk is a call that is routed by IP over the Internet through an Internet Telephony Service Provider (ITSP). For enterprises wanting to make full use of their installed IP PBXs' and communicate over IP not only within the enterprise, but also outside the enterprise—a SIP trunk provided by an ITSP that connects to the traditional PSTN network is the solution. Unlike traditional telephony, where bundles of physical wires were once delivered from the service provider to a business, a SIP trunk allows a company to replace traditional fixed PSTN lines with PSTN connectivity via a SIP trunking service provider on the Internet. SIP trunks can offer significant cost savings for enterprises, eliminating the need for local PSTN gateways, costly ISDN BRI's or PRI's. The following figure is an example of how our phone system operates: You can see that we have a local area network containing our desktops, servers, phones, and our 3CX Phone System. To reach the outside world using a SIP trunk, we have to go through our firewall or router. Depending on your network, you could be using a private IP address (10.x.x.x, 172.16.x.x, or 192.168.x.x) which is not allowed on the public Internet, so it has to get translated to the public IP address. This translation process is called Network Address Translation (NAT). Once we get outside the local network, we are in the public realm. Our ITSP uses the internet to get our phone call to/from the various carriers PSTN (analog) lines where our phone call is connected/terminated. There are three components necessary to successfully deploy SIP trunks: A PBX with a SIP-enabled trunk side An enterprise edge device understanding SIP An Internet Telephony or SIP trunking service provider The PBX In most cases, the PBX is an IP-based PBX, communicating with all endpoints over IP. However, it may just as well be a traditional digital or analog PBX. The sole requirement that has to be available is an interface for SIP trunking connectivity. The enterprise border element The PBX on the LAN connects to the ITSP via the enterprise border element. The enterprise edge component can either be a firewall with complete support for SIP, or an edge device connected to the firewall handling the traversal of the SIP traffic. The ITSP On the Internet, the ITSP provides connectivity to the PSTN for communication with mobile and fixed phones. Choosing a VoIP carrier—more than just price I feel two of the most important features to look for when choosing a VoIP carrier is support and call quality. Usually once you setup and everything is working, you won't need support. I always tell clients that there is no "boxed" solution that I can sell, every installation is a little different. Internet connections are all different even with the same provider. If you have a rock-solid T1 or something better, then this shouldn't be a problem. DSL seems different from building to building, even in the same area. So how do you test support before giving them your credit card? Call them! Try calling support at the worst times such as Monday afternoons when everyone is back to work and online, also try calling after business hours. See how long does it take to connect to a live person and if you can understand them once you speak to them? Find where is their support located? Try talking to them and tell them you are thinking about signing up with their service and ask them for help. If they go out of their way before they have your money, chances are they will be good to work with later on. Some carriers only offer chat or email support in favor of lower prices. While this may work fine for your business, it certainly won't work for the ones who need answers right away. I know I seem to be stressing a lot on support but it's for good reason. If your business depends on phone service and it goes down then you need answers! I pay more for a product if the support is worth it. Part of this is your Return On Investment (ROI). For example, if you have 3 lawyers billing at $200/hour and they need phones to work, that's $600/hour of lost time. Does the extra $50 or $100 upfront cover that? Now back to the topic at hand. Once you have connected 3CX to the carrier, how is the call quality? If it sounds like a bad cell phone, you probably don't want it, unless the price is so cheap that you can live with the low quality. Certain carriers even change the way your call gets routed through the Internet, based on the lowest cost for the particular call. They don't care about quality as long as you get that connection and they make money on it. Concurrent calls with an ITSP are a feature that you may want to look for when choosing an ITSP. Some accounts are a one-to-one ratio of lines per call. If you want to have 5 people on the phone at the same time (inbound or outbound), you would need to pay for 5 lines, this is similar to a PSTN line. You may get some savings here over a PSTN but that depends on what is available in your area. Some ITSP's have concurrent calls where you can use more than one line per call. Not many carriers have this feature but for a small business, this can be a great cost saving feature to look for. I use a couple of different carriers that have this feature. One carrier that I use lets you have 3 concurrent calls simultaneously on the same line. If you need more than 3 calls, you're a higher use customer and they want you to buy several lines. VoIP IP signaling uses special algorithms to compress your voice into IP packets. This compression uses a codec. There are several available, but the most common one is G.711u-law or A-law. This uses about 80kpbs of upload and download bandwidth. Another popular codec is G.729, it uses about 36kpbs. So for the same bandwidth you can have twice the number of calls using G.729 than G.711. You will need to check with your ITSP and see what codec they support. Another carrier I use is based purely on how much internet bandwidth you have. If you have 1Mbps of upload speed (usually the slowest part of your internet connection), you can support about 10 simultaneous or concurrent calls using G.711. You then pay for the minutes you use. This works very well for a small office as your monthly bill is very low and you don't have to maintain a bunch of lines that don't get used. Cable internet providers are also offering VoIP service to your home or business. These are usually single-use lines but they terminate at your office with an FXS plug. To integrate this with 3CX, you will need an FXO just like it's a PSTN line, same setup but you get the advantage of a VoIP line. Another great benefit of a SIP trunk is expandability. You can easily start out with one line which can usually be completed in one day. As you grow you can add more, usually in minutes as you already have the plan setup. Time to consolidate lines? You can even drop them later on without having contracts (most of the time). Try doing that with the local phone company! Call for a new business and it can take 1-2 weeks to get set up, plus contracts to worry about. No wonder they are jumping on the VoIP band wagon. Disaster recovery What do you do when your internet goes down? Some of you might be saying, "Ha! It never goes down". In my experience, it will eventually, and at the worst time. So what do you do? Go home for the day or plan for a backup? Most VoIP carriers provide some kind of disaster recovery option. They try to send you a call and when they don't get a connection to your 3CX box; then then re-route the call to another phone number. This could be a PSTN line or even a cell phone. It can be a free feature or there can be a small monthly fee on the account. It's worth having, especially if you rely on phones. Okay, so that covers inbound disaster recovery. What about outbound? Yes just about everyone has a cell phone these days, if that isn't enough, I'd suggest you invest in a pay-per-use PSTN line. This keeps the monthly cost very low but it's there when you need it. Whether it's an emergency pizza order for that Friday afternoon party or a true emergency when someone panics and dials 911—you want that call to go out. Speaking of emergency numbers, make sure you have your carrier register that phone number to your local address. Let's say you are in New York and you have a Californian phone number to give you some local presence in that part of the country. Your co-worker grabs his chest and falls down and someone dials 911 from the closest phone they see. Emergency services see your Californian number and contacts California for help for your New York office, that's not what you want when someone is clutching their chest, even though it was just heartburn from that pepperoni pizza. Mixing VoIP and PSTN Some of my clients even mix VoIP and PSTN together. Why would you mix? Local calls and inbound calls use the PSTN lines for the best call quality (and do not use any VoIP minutes if they have to pay for those). Long distance calls use the cheaper rate VoIP line. Another scenario is using PSTN lines for all your incoming and outgoing calls and use VoIP to talk to your other offices. Your own office can deal with a lower call quality, and management will appreciate the lower cost. These types of setups can be controlled using a dial plan.
Read more
  • 0
  • 0
  • 3982

article-image-trunks-using-3cx-part-2
Packt
12 Feb 2010
7 min read
Save for later

Trunks using 3CX: Part 2

Packt
12 Feb 2010
7 min read
The next wizard screen is for Outbound Call Rules. Let's go over it enough so that you can setup a simple rule. We start off with a name. This can be anything you like but I prefer something meaningful. For our example I want to dial 9 to use the analog line, and only allow extensions 100-102 to use this line. I also only want to be able to dial certain phone numbers. Then I have to delete the 9 before it goes out to the phone carrier. Let's have a look at each section of this screen: Calls to numbers starting with (Prefix) This is where you specify what you want someone to dial before the line is used. You could enter a string of numbers here to use as a "password" to dial out. You don't just let anyone call an international phone number, so set this to a string of numbers to use as your international password. Give the password only to those who need it. Just make sure you change it occasionally in case it slips out. Calls from extension(s) Now, you can specify who (by extension number) can use this gateway. Just enter the extension number(s) you want to allow either in a range (100-110), individually (100, 101, 104), or as a mix (100-103, 110). Usually, you will leave this open for everyone to use; otherwise, you will restrict extensions that were allowed to use the gateway, which will have repercussions of forwarding rules to external numbers. Calls to numbers with a length of This setting can be left blank if you want all calls to be able to go out on this gateway. In the next screenshot, I specified 3, 7, 10, and 11. This covers calls to 911, 411, 555-1234, 800-555-1234, and 1-800-555-1234, respectively. You can control what phone numbers go out based on the number of digits that are dialed. Route and strip options Since this is our only gateway right now, we will have it route the calls to the Patton gateway. The Strip Digits option needs to be set to 1. This will strip out the "9" that we specified above to dial out with. We can leave the Prepend section blank for now. Now, go ahead and click Finish: Once you click Finish, you will see a gateway wizard summary, as shown in the next screenshot. This shows you that the gateway is created, and it also gives an overview of the settings. Your next step is to get those settings configured on your gateway. There is a list of links for various supported gateways on the bottom of the summary page with up-to-date instructions. Feel free to visit those links. These links will take you to the 3CX website and explain how to configure that particular gateway. With Patton this is easy; click the Generate config file button. The only other information you need for the configuration file is the Subnet mask for the Patton gateway. Enter your network subnet mask in the box. Here, I entered a standard Class C subnet mask. This matches my 192.168.X.X network. Click OK when you are done: Once you click OK, your browser will prompt you to save the file, as shown in the following screenshot. Click Save: The following screenshot shows a familiar Save As Windows screen. I like to put this file in an easy-to-remember location on my hard drive. As I already have a 3CX folder created, I'm going to save the file there. You can change the name of the file if you wish. Click Save: Now that your file is saved, let's take a look at modifying those settings. Open the administration web interface and, on the left-hand side, click PSTN Devices. Go ahead and expand this by clicking the + sign next to it. Now, you will see our newly created Patton SN4114A gateway listed. Click the + sign again and expand that gateway. Next, click the Patton SN4114A name, and you will see the right-hand side window pane fill up with five separate tabs. The first tab is General. This is where you can change the gateway IP address, SIP port, and all the account details. If you change anything, you will need a new configuration file. So click the Generate config file button at the bottom of the screen. If you forgot to save the file previously, here's your chance to generate and save it again: On the Advanced tab, we have some Provider Capabilities. Leave these settings alone for now: We will leave the rest of the tabs for now. Go ahead and click the 10000 line information in the navigation pane on the left. These are the settings for that particular phone port (10000). The first group of settings that we can change is the authentication username and password. Remember, this is to register the line with 3CX and not to use the phone line. The next two sections are about what to do with an inbound call during Office Hours and Outside Office Hours. I didn't change anything from the gateway wizard but, on this screen, you can see that we selected Ring group 800 MainRingGroup. This is the Ring group that we configured previously. We also see similar drop-down boxes for Outside Office Hours. As no one will be in the office to answer the phone, I've selected a Digital Receptionist 801 DR1. In the section Other Options, the Outbound Caller ID box is used to enter what you would like to have presented to the outside world as caller ID information. If your phone carrier supports this, you can enter a phone number or a name. If the carrier does not support this, just leave it blank and talk to your carrier as to what you would require to have it assigned as your caller ID. The Allow outbound calls on this line and Allow incoming calls on this line checkboxes are used to limit calls in or out. Depending on your environment, you might want to leave one line selected as no outbound calls. This will always leave an incoming line for customers to call. Otherwise, unless you have other lines that they can call on, they will get a busy signal. Maximum simultaneous calls cannot be changed here as analog lines only support one call at a time. If you changed anything, click Apply and then go back and generate a new configuration file: For the most up-to-date information on configuring your gateway, visit the 3CX site: http://www.3cx.com/voip-gateways/index.html We will go over a summary of it here: Since nothing was changed, it is now time to configure the Patton device with the config file that we generated from the 3CX template. If you know the IP address of the device, go ahead and open a browser and navigate to that IP address. Mine would be http://192.168.2.10. If you do not know the IP address of your device, you will need the SmartNode discovery tool. The easiest place to get this tool is the CD that came with the device. You can also download it from http://www.3cx.com/downloads/misc/sndiscovery.zip, or search the Patton website for it. Go ahead and install the SmartNode discovery tool and run it. You will get a screen that tells you all the SmartNodes on your network with their IP address, MAC address, and firmware version. Double-click on the SmartNode to open the web interface in a browser. The default username is administrator, and the password field is left blank. Click Import/Export on the left and Import Configuration on the right. Click Browse to find the configuration file that we generated. Click Import and then Reload to restart the gateway with the new configuration. That's it . We can now get incoming calls and make an outbound call.
Read more
  • 0
  • 0
  • 2986
article-image-forms-grok-10
Packt
12 Feb 2010
13 min read
Save for later

Forms in Grok 1.0

Packt
12 Feb 2010
13 min read
A quick demonstration of automatic forms Let's start by showing how this works, before getting into the details. To do that, we'll add a project model to our application. A project can have any number of lists associated with it, so that related to-do lists can be grouped together. For now, let's consider the project model by itself. Add the following lines to the app.py file, just after the Todo application class definition. We'll worry later about how this fits into the application as a whole. class IProject(interface.Interface): name = schema.TextLine(title=u'Name',required=True) kind = schema.Choice(title=u'Kind of project', values=['personal','business']) description = schema.Text(title=u'Description')class AddProject(grok.Form): grok.context(Todo) form_fields = grok.AutoFields(IProject) We'll also need to add a couple of imports at the top of the file: from zope import interfacefrom zope import schema Save the file, restart the server, and go to the URL http://localhost:8080/todo/addproject. The result should be similar to the following screenshot: OK, where did the HTML for the form come from? We know that AddProject is some sort of a view, because we used the grok.context class annotation to set its context and name. Also, the name of the class, but in lowercase, was used in the URL, like in previous view examples. The important new thing is how the form fields were created and used. First, a class named IProject was defined. The interface defines the fields on the form, and the grok.AutoFields method assigns them to the Form view class. That's how the view knows which HTML form controls to generate when the form is rendered. We have three fields: name, description, and kind. Later in the code, the grok.AutoFields line takes this IProject class and turns these fields into form fields. That's it. There's no need for a template or a render method. The grok.Form view takes care of generating the HTML required to present the form, taking the information from the value of the form_fields attribute that the grok.AutoFields call generated. Interfaces The I in the class name stands for Interface. We imported the zope.interface package at the top of the file, and the Interface class that we have used as a base class for IProject comes from this package. Example of an interface An interface is an object that is used to specify and describe the external behavior of objects. In a sense, the interface is like a contract. A class is said to implement an interface when it includes all of the methods and attributes defined in an interface class. Let's see a simple example: from zope import interfaceclass ICaveman(interface.Interface): weapon = interface.Attribute('weapon') def hunt(animal): """Hunt an animal to get food""" def eat(animal): """Eat hunted animal""" def sleep() """Rest before getting up to hunt again""" Here, we are describing how cavemen behave. A caveman will have a weapon, and he can hunt, eat, and sleep. Notice that the weapon is an attribute—something that belongs to the object, whereas hunt, eat, and sleep are methods. Once the interface is defined, we can create classes that implement it. These classes are committed to include all of the attributes and methods of their interface class. Thus, if we say: class Caveman(object): interface.implements(ICaveman) Then we are promising that the Caveman class will implement the methods and attributes described in the ICaveman interface: weapon = 'ax'def hunt(animal): find(animal) hit(animal,self.weapon)def eat(animal): cut(animal) bite()def sleep(): snore() rest() Note that though our example class implements all of the interface methods, there is no enforcement of any kind made by the Python interpreter. We could define a class that does not include any of the methods or attributes defined, and it would still work. Interfaces in Grok In Grok, a model can implement an interface by using the grok.implements method. For example, if we decided to add a project model, it could implement the IProject interface as follows: class Project(grok.Container): grok.implements(IProject) Due to their descriptive nature, interfaces can be used for documentation. They can also be used for enabling component architectures, but we'll see about that later on. What is of more interest to us right now is that they can be used for generating forms automatically. Schemas The way to define the form fields is to use the zope.schema package. This package includes many kinds of field definitions that can be used to populate a form. Basically, a schema permits detailed descriptions of class attributes that are using fields. In terms of a form—which is what is of interest to us here—a schema represents the data that will be passed to the server when the user submits the form. Each field in the form corresponds to a field in the schema. Let's take a closer look at the schema we defined in the last section: class IProject(interface.Interface): name = schema.TextLine(title=u'Name',required=True) kind = schema.Choice(title=u'Kind of project', required=False, values=['personal','business']) description = schema.Text(title=u'Description', required=False) The schema that we are defining for IProject has three fields. There are several kinds of fields, which are listed in the following table. In our example, we have defined a name field, which will be a required field, and will have the label Name beside it. We also have a kind field, which is a list of options from which the user must pick one. Note that the default value for required is True, but it's usually best to specify it explicitly, to avoid confusion. You can see how the list of possible values is passed statically by using the values parameter. Finally, description is a text field, which means it will have multiple lines of text. Available schema attributes and field types In addition to title, values, and required, each schema field can have a number of properties, as detailed in the following table: Attribute Description title A short summary or label. description A description of the field. required Indicates whether a field requires a value to exist. readonly If True, the field's value cannot be changed. default The field's default value may be None, or a valid field value. missing_value If input for this field is missing, and that's OK, then this is the value to use. order The order attribute can be used to determine the order in which fields in a schema are defined. If one field is created after another (in the same thread), its order will be greater. In addition to the field attributes described in the preceding table, some field types provide additional attributes. In the previous example, we saw that there are various field types, such as Text, TextLine, and Choice. There are several other field types available, as shown in the following table. We can create very sophisticated forms just by defining a schema in this way, and letting Grok generate them. Field type Description Parameters Bool Boolean field.   Bytes Field containing a byte string (such as the python str). The value might be constrained to be within length limits.   ASCII Field containing a 7-bit ASCII string. No characters > DEL (chr(127)) are allowed. The value might be constrained to be within length limits.   BytesLine Field containing a byte string without new lines.   ASCIILine Field containing a 7-bit ASCII string without new lines.   Text Field containing a Unicode string.   SourceText Field for the source text of an object.   TextLine Field containing a Unicode string without new lines.   Password Field containing a Unicode string without new lines, which is set as the password.   Int Field containing an Integer value.   Float Field containing a Float.   Decimal Field containing a Decimal.   DateTime Field containing a DateTime.   Date Field containing a date.   Timedelta Field containing a timedelta.   Time Field containing time.   URI A field containing an absolute URI.   Id A field containing a unique identifier. A unique identifier is either an absolute URI or a dotted name. If it's a dotted name, it should have a module or package name as a prefix.   Choice Field whose value is contained in a predefined set. values: A list of text choices for the field. vocabulary: A Vocabulary object that will dynamically produce the choices. source: A different, newer way to produce dynamic choices. Note: only one of the three should be provided. More information about sources and vocabularies is provided later in this book. Tuple Field containing a value that implements the API of a conventional Python tuple. value_type: Field value items must conform to the given type, expressed via a field. Unique. Specifies whether the members of the collection must be unique. List Field containing a value that implements the API of a conventional Python list. value_type: Field value items must conform to the given type, expressed via a field. Unique. Specifies whether the members of the collection must be unique. Set Field containing a value that implements the API of a conventional Python standard library sets.Set or a Python 2.4+ set. value_type: Field value items must conform to the given type, expressed via a field. FrozenSet Field containing a value that implements the API of a conventional Python2.4+ frozenset. value_type: Field value items must conform to the given type, expressed via a field. Object Field containing an object value. Schema: The interface that defines the fields comprising the object. Dict Field containing a conventional dictionary. The key_type and value_type fields allow specification of restrictions for keys and values contained in the dictionary. key_type: Field keys must conform to the given type, expressed via a field. value_type: Field value items must conform to the given type, expressed via a field. Form fields and widgets Schema fields are perfect for defining data structures, but when dealing with forms sometimes they are not enough. In fact, once you generate a form using a schema as a base, Grok turns the schema fields into form fields. A form field is like a schema field but has an extended set of methods and attributes. It also has a default associated widget that is responsible for the appearance of the field inside the form. Rendering forms requires more than the fields and their types. A form field needs to have a user interface, and that is what a widget provides. A Choice field, for example, could be rendered as a <select> box on the form, but it could also use a collection of checkboxes, or perhaps radio buttons. Sometimes, a field may not need to be displayed on a form, or a writable field may need to be displayed as text instead of allowing users to set the field's value. Form components Grok offers four different components that automatically generate forms. We have already worked with the first one of these, grok.Form. The other three are specializations of this one: grok.AddForm is used to add new model instances. grok.EditForm is used for editing an already existing instance. grok.DisplayForm simply displays the values of the fields. A Grok form is itself a specialization of a grok.View, which means that it gets the same methods as those that are available to a view. It also means that a model does not actually need a view assignment if it already has a form. In fact, simple applications can get away by using a form as a view for their objects. Of course, there are times when a more complex view template is needed, or even when fields from multiple forms need to be shown in the same view. Grok can handle these cases as well, which we will see later on. Adding a project container at the root of the site To get to know Grok's form components, let's properly integrate our project model into our to-do list application. We'll have to restructure the code a little bit, as currently the to-do list container is the root object of the application. We need to have a project container as the root object, and then add a to-do list container to it. To begin, let's modify the top of app.py, immediately before the TodoList class definition, to look like this: import grokfrom zope import interface, schemaclass Todo(grok.Application, grok.Container): def __init__(self): super(Todo, self).__init__() self.title = 'To-Do list manager' self.next_id = 0 def deleteProject(self,project): del self[project] First, we import zope.interface and zope.schema. Notice how we keep the Todo class as the root application class, but now it can contain projects instead of lists. We also omitted the addProject method, because the grok.AddForm instance is going to take care of that. Other than that, the Todo class is almost the same. class IProject(interface.Interface): title = schema.TextLine(title=u'Title',required=True) kind = schema.Choice(title=u'Kind of project',values=['personal', 'business']) description = schema.Text(title=u'Description',required=False) next_id = schema.Int(title=u'Next id',default=0) We then have the interface definition for IProject, where we add the title, kind, description, and next_id fields. These were the fields that we previously added during the call to the __init__ method at the time of product initialization. class Project(grok.Container): grok.implements(IProject) def addList(self,title,description): id = str(self.next_id) self.next_id = self.next_id+1 self[id] = TodoList(title,description) def deleteList(self,list): del self[list] The key thing to notice in the Project class definition is that we use the grok.implements class declaration to see that this class will implement the schema that we have just defined. class AddProjectForm(grok.AddForm): grok.context(Todo) grok.name('index') form_fields = grok.AutoFields(Project) label = "To begin, add a new project" @grok.action('Add project') def add(self,**data): project = Project() self.applyData(project,**data) id = str(self.context.next_id) self.context.next_id = self.context.next_id+1 self.context[id] = project return self.redirect(self.url(self.context[id])) The actual form view is defined after that, by using grok.AddForm as a base class. We assign this view to the main Todo container by using the grok.context annotation. The name index is used for now, so that the default page for the application will be the 'add form' itself. Next, we create the form fields by calling the grok.AutoFields method. Notice that this time the argument to this method call is the Project class directly, rather than the interface. This is possible because the Project class was associated with the correct interface when we previously used grok.implements. After we have assigned the fields, we set the label attribute of the form to the text: To begin, add a new project. This is the title that will be shown on the form. In addition to this new code, all occurrences of grok.context(Todo) in the rest of the file need to be changed to grok.context(Project), as the to-do lists and their views will now belong to a project and not to the main Todo application. For details, take a look at the source code of this article for Grok 1.0 Web Development>>Chapter 5.
Read more
  • 0
  • 0
  • 1881

article-image-testing-and-debugging-grok-10-part-2
Packt
11 Feb 2010
8 min read
Save for later

Testing and Debugging in Grok 1.0: Part 2

Packt
11 Feb 2010
8 min read
Adding unit tests Apart from functional tests, we can also create pure Python test cases which the test runner can find. While functional tests cover application behavior, unit tests focus on program correctness. Ideally, every single Python method in the application should be tested. The unit test layer does not load the Grok infrastructure, so tests should not take anything that comes with it for granted; just the basic Python behavior. To add our unit tests, we'll create a module named unit_tests.py. Remember, in order for the test runner to find our test modules, their names have to end with 'tests'. Here's what we will put in this file: """ Do a Python test on the app. :unittest: """ import unittest from todo.app import Todo class InitializationTest(unittest.TestCase): todoapp = None def setUp(self): self.todoapp = Todo() def test_title_set(self): self.assertEqual(self.todoapp.title,u'To-do list manager') def test_next_id_set(self): self.assertEqual(self.todoapp.next_id,0) The :unittest: comment at the top, is very important. Without it, the test runner will not know in which layer your tests should be executed, and will simply ignore them. Unit tests are composed of test cases, and in theory, each should contain several related tests based on a specific area of the application's functionality. The test cases use the TestCase class from the Python unittest module. In these tests, we define a single test case that contains two very simple tests. We are not getting into the details here. Just notice that the test case can include a setUp and a tearDown method that can be used to perform any common initialization and destruction tasks which are needed to get the tests working and finishing cleanly. Every test inside a test case needs to have the prefix 'test' in its name, so we have exactly two tests that fulfill this condition. Both of the tests need an instance of the Todo class to be executed, so we assign it as a class variable to the test case, and create it inside the setUp method. The tests are very simple and they just verify that the default property values are set on instance creation. Both of the tests use the assertEqual method to tell the test runner that if the two values passed are different, the test should fail. To see them in action, we just run the bin/test command once more: $ bin/testRunning tests at level 1 Running todo.FunctionalLayer tests: Set up in 2.691 seconds. Running: .......2009-09-30 22:00:50,703 INFO sqlalchemy.engine.base.Engine.0x...684c PRAGMA table_info("users") 2009-09-30 22:00:50,703 INFO sqlalchemy.engine.base.Engine.0x...684c () Ran 7 tests with 0 failures and 0 errors in 0.420 seconds. Running zope.testing.testrunner.layer.UnitTests tests: Tear down todo.FunctionalLayer ... not supported Running in a subprocess. Set up zope.testing.testrunner.layer.UnitTests in 0.000 seconds. Ran 2 tests with 0 failures and 0 errors in 0.000 seconds. Tear down zope.testing.testrunner.layer.UnitTests in 0.000 seconds. Total: 9 tests, 0 failures, 0 errors in 5.795 seconds Now, both the functional and unit test layers contain some tests and both are run one after the other. We can see the subtotal for each layer at the end of its tests as well as the grand total of the nine passed tests when the test runner finishes its work. Extending the test suite Of course, we just scratched the surface of which tests should be added to our application. If we continue to add tests, hundreds of tests may be there by the time we finish. However, this article is not the place to do so. As mentioned earlier, its way easier to have tests for each part of our application, if we add them as we code. There's no hiding from the fact that testing is a lot of work, but there is great value in having a complete test suite for our applications. More so, when third parties might use our work product independently. Debugging We will now take a quick look at the debugging facilities offered by Grok. Even if we have a very thorough test suite, chances are there that we will find a fair number of bugs in our application. When that happens, we need a quick and effective way to inspect the code as it runs and find the problem spots easily. Often, developers will use print statements (placed at key lines) throughout the code, in the hopes of finding the problem spot. While this is usually a good way to begin locating sore spots in the code, we often need some way to follow the code line by line to really find out what's wrong. In the next section, we'll see how to use the Python debugger to step through the code and find the problem spots. We'll also take a quick look at how to do post-mortem debugging in Grok, which means jumping into the debugger to analyze program state immediately after an exception has occurred. Debugging in Grok For regular debugging, where we need to step through the code to see what's going on inside, the Python debugger is an excellent tool. To use it, you just have to add the next line at the point where you wish to start debugging: import pdb; pdb.set_trace() Let's try it out. Open the app.py module and change the add method of the AddProjectForm class (line 108) to look like this: @grok.action('Add project') def add(self,**data): import pdb; pdb.set_trace() project = Project() project.creator = self.request.principal.title project.creation_date = datetime.datetime.now() project.modification_date = datetime.datetime.now() self.applyData(project,**data) id = str(self.context.next_id) self.context.next_id = self.context.next_id+1 self.context[id] = project return self.redirect(self.url(self.context[id])) Notice that we invoke the debugger at the beginning of the method. Now, start the instance, go to the 'add project' form, fill it up, and submit it. Instead of seeing the new project view, the browser will stay at the 'add form' page, and display the waiting for... message. This is because control has been transferred to the console for the debugger to act. Your console will look like this: > /home/cguardia/work/virtual/grok1/todo/src/todo/app.py(109)add() -> project = Project() (Pdb) | The debugger is now active and waiting for input. Notice that the line number where debugging started, appears right beside the path of the module where we are located. After the line number, comes the name of the method, add(). Below that, the next line of code to be executed is shown. The debugger commands are simple. To execute the current line, type n: (Pdb) n > /home/cguardia/work/virtual/grok1/todo/src/todo/app.py(110)add() -> project.creator = self.request.principal.title (Pdb) You can see the available commands if you type h: (Pdb) h Documented commands (type help <topic>): ======================================== EOF break condition disable help list q step w a bt cont down ignore n quit tbreak whatis alias c continue enable j next r u where args cl d exit jump p return unalias b clear debug h l pp s up Miscellaneous help topics: ========================== exec pdb Undocumented commands: ====================== retval rv (Pdb) The list command id is used for getting a bird's eye view of where in the code are we: (Pdb) list 105 106 @grok.action('Add project') 107 def add(self,**data): 108 import pdb; pdb.set_trace() 109 project = Project() 110 -> project.creator = self.request.principal.title 111 project.creation_date = datetime.datetime.now() 112 project.modification_date = datetime.datetime.now() 113 self.applyData(project,**data) 114 id = str(self.context.next_id) 115 self.context.next_id = self.context.next_id+1 (Pdb) As you can see, the current line is shown with an arrow. It's possible to type in the names of objects within the current execution context and find out their values: (Pdb) project <todo.app.Project object at 0xa0ef72c> (Pdb) data {'kind': 'personal', 'description': u'Nothing', 'title': u'Project about nothing'} (Pdb) We can of course, continue stepping line by line through all of the code in the application, including Grok's own code, checking values as we proceed. When we are through reviewing, we can click on c to return control to the browser. At this point, we will see the project view. The Python debugger is very easy to use and it can be invaluable for finding obscure bugs in your code.
Read more
  • 0
  • 0
  • 3018
Modal Close icon
Modal Close icon