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

How-To Tutorials - Web Development

1802 Articles
article-image-feeds-facebook-applications
Packt
16 Oct 2009
7 min read
Save for later

Feeds in Facebook Applications

Packt
16 Oct 2009
7 min read
{literal} What Are Feeds? Feeds are the way to publish news in Facebook. As we have already mentioned before, there are two types of feeds in Facebook, News feed and Mini feed. News feed instantly tracks activities of a user's online friends, ranging from changes in relationship status to added photos to wall comments. Mini feed appears on individuals' profiles and highlights recent social activity. You can see your news feed right after you log in, and point your browser to http://www.facebook.com/home.php. It looks like the following, which is, in fact, my news feed. Mini feeds are seen in your profile page, displaying your recent activities and look like the following one: Only the last 10 entries are being displayed in the mini feed section of the profile page. But you can always see the complete list of mini feeds by going to http://www.facebook.com/minifeed.php. Also the mini feed of any user can be accessed from http://www.facebook.com/minifeed.php?id=userid. There is another close relation between news feed and mini feed. When applications publish a mini feed in your profile, it will also appear in your friend's news feed page. How to publish Feeds Facebook provides three APIs to publish mini feeds and news feeds. But these are restricted to call not more than 10 times for a particular user in a 48 hour cycle. This means you can publish a maximum of 10 feeds in a specific user's profile within 48 hours. The following three APIs help to publish feeds: feed_publishStoryToUser—this function publishes the story to the news feed of any user (limited to call once every 12 hours). feed_publishActionOfUser—this one publishes the story to a user's mini feed, and to his or her friend's news feed (limited to call 10 times in a rolling 48 hour slot). feed_publishTemplatizedAction—this one also publishes mini feeds and news feeds, but in an easier way (limited to call 10 times in a rolling 48 hour slot). You can test this API also from http://developers.facebook.com/tools.php?api, and by choosing Feed Preview Console, which will give you the following interface: And once you execute the sample, like the previous one, it will preview the sample of your feed. Sample application to play with Feeds Let's publish some news to our profile, and test how the functions actually work. In this section, we will develop a small application (RateBuddies) by which we will be able to send messages to our friends, and then publish our activities as a mini feed. The purpose of this application is to display friends list and rate them in different categories (Awesome, All Square, Loser, etc.). Here is the code of our application: index.php<?include_once("prepend.php"); //the Lib and key container?><div style="padding:20px;"><?if (!empty($_POST['friend_sel'])){ $friend = $_POST['friend_sel']; $rating = $_POST['rate']; $title = "<fb:name uid='{$fbuser}' useyou='false' /> just <a href='http://apps.facebook.com/ratebuddies/'>Rated</a> <fb:name uid='{$friend}' useyou='false' /> as a '{$rating}' "; $body = "Why not you also <a href='http://apps.facebook.com/ratebuddies/'>rate your friends</a>?";try{//now publish the story to user's mini feed and on his friend's news feed $facebook->api_client->feed_publishActionOfUser($title, $body, null, $null,null, null, null, null, null, null, 1); } catch(Exception $e) { //echo "Error when publishing feeds: "; echo $e->getMessage(); }}?> <h1>Welcome to RateBuddies, your gateway to rate your friends</h1> <div style="padding-top:10px;"> <form method="POST"> Seect a friend: <br/><br/> <fb:friend-selector uid="<?=$fbuser;?>" name="friendid" idname="friend_sel" /> <br/><br/><br/> And your friend is: <br/> <table> <tr> <td valign="middle"><input name="rate" type="radio" value="funny" /></td> <td valign="middle">Funny</td> </tr> <tr> <td valign="middle"><input name="rate" type="radio" value="hot tempered" /></td> <td valign="middle">Hot Tempered</td> </tr> <tr> <td valign="middle"><input name="rate" type="radio" value="awesome" /></td> <td valign="middle">Awesome</td> </tr> <tr> <td valign="middle"><input name="rate" type="radio" value="naughty professor" /></td> <td valign="middle">Naughty Professor</td> </tr> <tr> <td valign="middle"><input name="rate" type="radio" value="looser" /></td> <td valign="middle">Looser</td> </tr> <tr> <td valign="middle"><input name="rate" type="radio" value="empty veseel" /></td> <td valign="middle">Empty Vessel</td> </tr> <tr> <td valign="middle"><input name="rate" type="radio" value="foxy" /></td> <td valign="middle">Foxy</td> </tr> <tr> <td valign="middle"><input name="rate" type="radio" value="childish" /></td> <td valign="middle">Childish</td> </tr> </table> &nbsp; <input type="submit" value="Rate Buddy"/> </form> </div></div> index.php includes another file called prepend.php. In that file, we initialized the facebook api client using the API key and Secret key of the current application. It is a good practice to keep them in separate file because we need to use them throughout our application, in as many pages as we have. Here is the code of that file: prepend.php<?php// this defines some of your basic setupinclude 'client/facebook.php'; // the facebook API library// Get these from ?http://www.facebook.com/developers/apps.phphttp://www.facebook.com/developers/apps.php$api_key = 'your api key';//the api ket of this application$secret = 'your secret key'; //the secret key$facebook = new Facebook($api_key, $secret); //catch the exception that gets thrown if the cookie has an invalid session_key in it try { if (!$facebook->api_client->users_isAppAdded()) { $facebook->redirect($facebook->get_add_url()); } } catch (Exception $ex) { //this will clear cookies for your application and redirect them to a login prompt $facebook->set_user(null, null); $facebook->redirect($appcallbackurl); }?> The client is a standard Facebook REST API client, which is available directly from Facebook. If you are not sure about these API keys, then point your browser to http://www.facebook.com/developers/apps.php and collect the API key and secret key from there. Here is a screenshot of that page: Just collect your API key and Secret Key from this page, when you develop your own application. Now, when you point your browser to http://apps.facebooks.com/ratebuddies and successfully add that application, it will look like this: To see how this app works, type a friend in the box, Select a friend, and click on any rating such as Funny or Foxy. Then click on the Rate Buddy button. As soon as the page submits, open your profile page and you will see that it has published a mini feed in your profile.
Read more
  • 0
  • 0
  • 5373

article-image-add-tools-and-theming-tips-plone-3
Packt
16 Oct 2009
10 min read
Save for later

Add-on Tools and Theming Tips for Plone 3

Packt
16 Oct 2009
10 min read
(For more resources on Plone, see here.) Popular add-on Plone products One of the best things about open source development is the proliferation of products to solve common use cases. Plone themers are fortunate to have a solid set of tools available to them to solve these use cases. Enabling drop downs using webcouturier.dropdownmenu As most themers know, a lot of clients desire drop-down menus. In the past, this required coding HTML strings and the use of a product by Quintagroup named qPloneDropDownMenu. This product is still the recommended drop-down menu product for Plone 2.5x, but for 3.x, the real star is Denys Mishunov's product, webcouturier.dropdownmenu. The joy of this product is that you install it, and it works instantly. The product works by subclassing the globalsections viewlet via the following code, found in the browser/configure.zcml file of the webcouturier.dropdownmenu product: <!-- Override global sections viewlet --> <browser:viewlet name="plone.global_sections" manager="plone.app.layout.viewlets.interfaces.IPortalHeader" class=".dropdown.DropdownMenuViewlet" layer=".interfaces.IDropdownSpecific" permission="zope2.View" /> In the event that you've already customized your globalsections viewlet, you will have to subclass the DropdownMenuViewlet class in the webcouturier.dropdownmenu product. Unlike older drop-down menu products, webcouturier.dropdownmenu does not require any ongoing maintenance or manual adjustment of URLs. It is controlled by the navigation settings found in the Site Setup area, so you can control what types of items display in the navigation. The product also provides some basic CSS styling that can be easily adjusted in your own theme product, if desired. It can be downloaded here: http://plone.org/products/webcouturier-dropdownmenu/ Collage Another helpful Plone product is Malthe Borch's Collage. Collage allows you to create a grid containing rows and columns, and within those columns you can pull in the contents of other objects—a folder, a page, a collection, or even an image. Using this mechanism, you can create a complex page layout without knowing any programming. Until very recently, Collage did not have hooks that allowed it to be styled using CSS, and it did not respect different views. For example, if you created a special mysection_view.pt (same as a homepage_view), and you assigned that view to your page, Collage would default to the original document_view. This behavior has now been altered so that CSS hooks are available and different views are respected. This is a huge win for sites that are heavily styled and need to maintain consistency. It's suggested that when using Collage, you do not create your objects within the Collage itself; you should instead create the objects in your normal Plone content tree, and pull those items in as aliases. The reason for suggesting this is that it is not possible to access the contents of a Collage via the standard folder_contents view that is normally possible in a folder. Hence, if you need to move that content to another area of your site, you cannot. This also invites some jeopardy when migrating to a new version of Plone. It's worth mentioning that Collage will not become part of core Plone in the future, as the mechanism for organizing blocks of content on a page in the future will be accomplished via a new drag-and-drop mechanism. The lead programmer for Collage has stated, however, that there will be a migration path, but the reality of this is unknown. Finally, the usability of the Collage product is a bit clunky, but with some common sense, it's easy to use and can be a quite powerful layout tool for Plone 3. It can be downloaded here: http://plone.org/products/collage. Tableless styling using Plone Tableless A popular product for CSS purists is Simon Kaeser's plone.tableless. Plone's default main_template is created using tables, which many themers do not wish to use. To get a tableless version of Plone's main_template, simply install this product; make sure your site's portal_css is in debug mode, and test the following code: #portal-column-one {float:right;}#portal-column-two {float:left;} If you're able to switch the position of these two columns, the product works and you can style in full tableless mode. There are a few issues with Plone and tableless layouts that are unrelated to this product, but in general it works. As of this writing, the product was not tested against some of the newer browsers. It can be downloaded here: http://plone.org/products/plone-tableless/. CSSManager End users often want to have some control over basic modifications to their site—background color, link colors, and so on. The WebLion Group from Penn State University created CSSManager, a product that gives a simple, through the web interface to let users change the colors, borders, site logo, and other visual characteristics of their Plone site. Essentially, it uses the DTML variables defined in the base_properties.props file available within Plone. The product can be downloaded here: http://plone.org/products/cssmanager. To use it, install the product, go to your site's Site Setup area, and find the configlet for this tool, and try changing a few options. The CSSManager tool will supersede a theme product's base_properties if the CSSManager skin layer is above the theme product's skin layers in portal_skins/properties in the ZMI. If uninstalled, your settings can still be found in the custom folder for your Plone site via this URL: http://localhost:8080/mysite/portal_skins/custom/base_properties/manage_propertiesForm. So you can feel confident removing it if you no longer need it. Products.EasyAsPiIE Until IE7, there was no fully native support for PNG alpha channel transparency in Internet Explorer. However, since IE5.5, there has been some support in the form of a proprietary filter called AlphaImageLoader. Internet Explorer filters can be applied directly in your CSS (for both inline and background images), or by setting the same CSS property with JavaScript. Unfortunately, there's no CSS property called filter in the W3C CSS spec. It's a proprietary extension added by Microsoft that could potentially cause other browsers to reject your entire CSS rule. Also, AlphaImageLoader does not magically add full PNG transparency support so that a PNG in the page will just start working. Instead, when applied to an element in the page, it draws a new rendering surface in the same space that element occupies and loads a PNG into it. If that sounds weird, it is. However, by and large the result is that PNGs with an alpha channel can be accommodated. The WebLion Group's Products.EasyAsPiIE product uses this filter approach to handle transparent PNGs with IE6. All it does is enable JavaScript written by Angus Turnbull: http://www.twinhelix.com. You can download it from here: http://plone.org/products/productseasyaspiie/ and follow the installation instructions. Optionally, if you choose not to use this product, you can also just export to PNG8 format, instead of PNG24, to get around IE6 problems, and of course there are a lot of alternative solutions out there as well. You can read more about PNGs here: http://www.sitepoint.com/blogs/2007/09/18/png8-the-clear-winner/. Both Photoshop and Fireworks can export to PNG8, though other graphical programs may not. collective.skinny Another Plone product that has surfaced is Daniel Nouri's collective.skinny, which can be downloaded from http://plone.org/products/collectiveskinny/. This product is an example implementation of a separate, public-facing skin that abstracts away some of the complexity of the theming process. According to the product page, it's been described as being vastly easier than skinning Plone the conventional way, but it also has a few drawbacks. For example, you can't use it for community sites where people other than your site editors log in and modify content. It's also a little confusing from my perspective, but it's a product adventurous themers might pay attention to. It's probable that Deliverance and collective.xdv (the future of theming for Plone) will make this product obsolete, as Deliverance removes a lot of complexity and makes theming accessible to individuals who don't even know what Plone is. FS Dump For themers who started their skin creation through the web or who have content they wish to extract from the ZMI, FS Dump is an excellent tool. To use it, download the product from http://www.plone.org/products/fsdump and follow the installation instructions. This is a product that lives in the Products namespace, so it is not installed like egg-based products. Once installed, use the Add drop-down menu, found at http://www.mysite.com/manage_main, and create a Dumper instance in a folder (or product) that contains the TTW code to be dumped. This tool appears to work best when trying to dump items from the custom/ folder, though, hypothetically, it should work for any other folder in the ZMI. Next, supply an absolute path to a directory on the filesystem in which the dumper is to create the files, for example /opt. (Note that the user for whom Zope is running needs write access to this directory.) Click the Change and Dump button to do the dump to the indicated directory, and then copy the dumped files into your theme product's skins/ folder in the appropriate locations. qPloneSkinDump Another popular dumper product is known as Plone Skin Dump (qPloneSkinDump) by Quintagroup. Plone Skin Dump allows users to create a Plone product in the Products namespace by exporting the custom/ folder. It creates an old-school Plone theme product for you, but it does not provide the plonetheme-type of product. The product has not been tested against Plone 3, so it may not be the best option. Moreover, at the time this article was written, it was not possible to download the product from its SourceForge repository. In the event you wish to try this product, you can find it here: http://plone.org/products/plone-skin-dump. Again, it is in the Products namespace, so all you need to do is drop it in your buildout's products/ folder. You can then follow the directions posted on the product page. It's a bit more complicated than FS Dump, but obviously it does a bit more. Collection and static portlets While portlets are not add-on products, they are tools that can greatly enhance the impact of your site and worth mentioning. Default Plone provides collection and static portlets that can be added on any page by clicking on the Manage Portlets link on your site. These portlets provide great power and can be styled using CSS. A collection portlet, for example, can be set to display random contents fitting certain criteria—maybe a randomized spotlight content type tagged with a special keyword. This keeps the look and feel of a site fresh and gives some power to the end user. These portlets have the same structure as other portlets, so they will use any default styling that may be applied.
Read more
  • 0
  • 0
  • 2064

article-image-joomla-15-template-reference-part-1
Packt
16 Oct 2009
2 min read
Save for later

Joomla! 1.5 Template Reference: Part 1

Packt
16 Oct 2009
2 min read
  Take note that we'll see how these Joomla! 1.5 items differ in use from a Joomla! 1.0 template, so that those of you looking to update a Joomla! 1.0 template to 1.5 can quickly get a handle on what to update in your templates and what new features to add. Consider this article your "cheat sheet". Jdoc include tags The jdoc include tags are new to Joomla! 1.5 templates. Previously in Joomla! 1.0, more complicated, abstract PHP code, originally created for Mambo, was used. The jdoc tags are much cleaner, visually make sense (no more guessing what attribute values like "-3" mean), and, thus, are much easier to remember. Site header information tag This is pretty simple: the tag outputs all the appropriate meta tags and header information that corresponds to your site and each individual page: <jdoc:include type="head" /> Joomla! 1.0 to 1.5 conversion If you're converting a 1.0 template to 1.5, you'll replace this PHP function in your 1.0 template's header with the above jdoc tag: <head>...<?php mosShowHead(); ?>... The component include tag Wherever you place this include, all component content will appear (from articles to poll results to contact forms, and so on): <jdoc:include type="component" /> Joomla! 1.0 to 1.5 conversion The 1.0 equivalent of this tag is the mosMainBody function. You'll replace this PHP function with the above jdoc include: <?php mosMainBody(); ?> Module position tags With module tags, we have a few options to work with. So, we can control what modules load into the area, thus assigning their positions as well as what style to output the module content with: <jdoc:include type="modules" name="position" style="styleName" /> Module position styles In the jdoc include example above, within the style attribute, you can place one of the following six style names to various effect:
Read more
  • 0
  • 0
  • 1673

article-image-joomla-15-template-reference-part-2
Packt
16 Oct 2009
4 min read
Save for later

Joomla! 1.5 Template Reference: Part 2

Packt
16 Oct 2009
4 min read
Common Joomla! CSS As you can see, via template overrides, you can pretty much define any CSS ids or classes you want. For those of you who are into creating and tweaking template overrides, unless you're going to create a highly custom, private, not-for-the-public template, my recommendation is you continue to use Joomla's general CSS ids and classes for component and module output as much as possible. This is a good way to ensure your template is familiar to other Joomla! administrators, especially if you want to offer your template to the public or for commercial sale. It's easy for them to look up and customize CSS rules rather than forcing them to discover all the new and interestingly-named CSS ids and classes you created. For those of us working with Joomla's core output or the Beez template overrides (which attempts to use Joomla's standard CSS), here is a list of some of the most common CSS ids and classes. Those of you familiar with Joomla! 1.0 template design will be pleased to find these haven't really changed. This list has been put together after a bit of research and a lot of experimentation with the Web Developer Toolbar CSS tools. It is probably not complete, but if you account for these items in your CSS rules, you'll be pretty well covered for most Joomla! projects, and it will be easy to spot any ids or classes not covered here and add them to your CSS sheet. The Joomla.org forum has a post with a fairly comprehensive list, most of which you'll recognize here, so it's definitely worth checking out: http://forum.joomla.org/viewtopic.php?t=125508. Joomla! 1.5 CSS ids #active_menu This is generated by the type="modules" include. Use it to style and control the currently selected main menu item. #blockrandom This is generated by the type="component" include when you're using the wrapper. This is the iFrame's id. #contact_email_copy This is generated by the type="component" include when you're in the contact form page view. This is a field name id. #contact_text This is generated by the type="component" include when you're in the contact form page view. This is a field name id. #emailForm This is generated by the type="component" include when you're in the contact form page view. This is a field name id. #mainlevel This is generated by the type="modules" include. Use it to style and control the main menu div holding each main menu item. #mod_login_password This is generated by the type="modules" include. This is a field name id. #mod_login_remember This is generated by the type="modules" include. This is a field name id. #mod_login_username This is generated by the type="modules" include. This is a field name id. #poll This is generated by the type="modules" include by the poll module. You can control the placement of the entire id with this. #search_ordering This is generated by the type="component" include when you're in the search form page view. This is a field name id. #search_searchword This is generated by the type="component" include when you're in the search form page view. This is a field name id. #searchphraseall This is generated by the type="component" include when you're in the search form page view. This is a field name id. #searchphraseany This is generated by the type="component" include when you're in the search form page view. This is a field name id. #searchphraseexact This is generated by the type="component" include when you're in the search form page view. This is a field name id. #voteid1,#voteid2,#voteid3, and so on This is generated by the type="modules" include. This is generated by the poll module and are field name ids for the radio buttons.    
Read more
  • 0
  • 0
  • 1784

article-image-managing-and-enhancing-multi-author-blogs-wordpress-27part-1
Packt
16 Oct 2009
18 min read
Save for later

Managing and Enhancing Multi-Author Blogs with WordPress 2.7(Part 1)

Packt
16 Oct 2009
18 min read
Creating an author page template If you have different authors on your blog, then my suggestion to you would be to display the biographical and contact information of each author on his own dedicated page. Luckily, WordPress allow us to do just that. Getting ready In this recipe, we're going to create an author page template for the purpose of displaying author related information. Make sure that you have understood the creation and usage of a page template. How to do it Create a new file named authors.php on your WordPress theme directory. Insert the following code into your file named authors.php: <?php/*Template Name: Authors Page*/?><?php get_header(); ?><div id="content" class="narrowcolumn"><?phpif(isset($_GET['author_name'])) :$curauth = get_userdatabylogin($author_name);else :$curauth = get_userdata(intval($author));endif;?><h2>About <?php echo $curauth->nickname; ?></h2><div class="excerpt"><?php echo $curauth->nickname; ?> personal website:<a href="<?php echo $curauth->user_url; ?>"><?php echo $curauth->user_url; ?></a></div><?php echo $curauth->user_description; ?><h2>Latest posts by <?php echo $curauth->nickname; ?>:</h2>Chapter 6133<ul><?php if ( have_posts() ) : while ( have_posts() ) :the_post(); ?><li><a href="<?php the_permalink() ?>"><?php the_title();?></a> on <?php the_time('d M Y'); ?></li><?php endwhile; else: ?><p><?php _e('No posts by this author.'); ?></p><?php endif; ?></ul></div><!--/content--><?php get_sidebar(); ?><?php get_footer(); ?> Save the file and upload it to the wp-content/themes/yourtheme folder of your WordPress install. Log in to your WordPress dashboard, create a new page, and select the Authors Page as a page template. Give it the title of your choice, such as, About the Author and publish the page. Open the single.php file from your theme. Depending on the theme that you're using, you may need to add the following code in order to display the author's name and a link to the author's page: Posted by <?php the_author_posts_link(); ?> Once you have saved the modifications made in your single.php file, visit one of your blog posts and click on the author name. The author page is displayed showing the author name, description, and web site. How it works The first thing that we need to know is the name of the author whose information is to be displayed. To do so, we have to get the author_name parameter sent via the GET method. With this value, we can initialize a $curauth php object that will allow us to get some personal information about the author, such as his web site, email, biography, and so on, with the help of the classic php syntax, that is, $curauth->nickname;. Once the author data, that is to be displayed, has been retrieved, we shall add a WordPress loop in order to be able to view the recent posts by this author. The following screenshot shows a well-prepared author page: There's more... In the preceding example we retrieved the author name, description, and web site URL. However, as you may know, users can provide much more information (in Administration, Profile, Your Profile options) such as their email address, AIM and Yahoo! messenger nickname, and login information. A few more template tags can be used to retrieve another kind of information from the author data. These tags are listed under the There's more! section of Displaying author-related information on posts, which we will see later in this article. Displaying a custom login form in your blog's sidebar It doesn't matter whether you're running a multi-author blog, or a blog where readers can register. Having a login form embedded in your sidebar will make your blog look a lot more professional and user friendly. Here is what you can expect from this recipe. In the following screenshot, a login form has been added to the K2 theme sidebar. Getting ready To achieve this recipe, you'll have to edit the sidebar.php file from your theme. The following hack works with WordPress 2.0 to 2.8. How to do it Open the sidebar.php file for editing. Find the opening <ul> tag and paste the following code under it: <li><?php global $user_ID, $user_identity, $user_level ?><?php if ( $user_ID ) : ?><h2><?php echo $user_identity ?></h2><ul><li><a href="<?php bloginfo('url') ?>/wp-login.php?action=logout&amp;redirect_to=<?php echo urlencode($_SERVER['REQUEST_URI']) ?>">Logout</a></li></ul><?php elseif ( get_option('users_can_register') ) : ?>Managing and Enhancing Multi-Author Blogs136<h2>Identification</h2><ul><li><form action="<?php bloginfo('url')?>/wp-login.php" method="post"><p><label for="log"><input type="text" name="log"id="log" value="<?php echo wp_specialchars (stripslashes($user_login), 1) ?>" size="22" /> User</label><br /><label for="pwd"><input type="password"name="pwd" id="pwd" size="22" /> Password</label><br /><input type="submit" name="submit" value="Login"class="button" /><label for="rememberme"><input name="rememberme"id="rememberme" type="checkbox" checked="checked"value="forever" /> Remember me</label><br /></p><input type="hidden" name="redirect_to" value="<?php echo$_SERVER['REQUEST_URI']; ?>"/></form></li><li><a href="<?php bloginfo('url')?>/wp-register.php">Register</a></li><li><a href="<?php bloginfo('url') ?>/wp-login.php?action=lostpassword">Recover password</a></li></ul><?php endif ?></li> Save the file. Your users can now login directly from your blog's sidebar. How it works The working of this code is quite simple. First, you initialize the global variables to get the user ID, name, and level. Then, you check the value of the $user_ID variable. If the value is not null, which means that the current user is logged in, you then display a quick hello user text and a link to log out. If the user isn't logged in, you check whether registering is allowed on the blog. If the user is logged in, then you simply display an HTML form that allows the user to log in directly from the blog. A link has also been included for registration if the current user doesn't have an account yet. This code was inspired from a tutorial available at www.wpdesigner.com. Adding a control panel to your blog's sidebar Now that you have learned how to check whether a user is logged in or not, why not learn how to add a small control panel to your blog's sidebar that is only visible to the logged in users. In this recipe, you'll learn how to achieve this task. Getting ready The upcoming piece of code works in exactly the same way as the code from the previous recipe does. It is all about checking if the user is logged in and whether he or she has the right to do a certain kind of thing. The following screenshot shows a simple, but useful, control panel which is similar to the one we're about to create: How to do it Open sidebar.php for editing. Find the first opening <ul> HTML tag, and paste the following code under the <ul> tag: <li><?php global $user_ID, $user_identity, $user_level ?><?php if ( $user_ID ) : ?><h2>Control panel</h2><ul><li>Identified as <strong><?php echo $user_identity ?></strong>.<ul><li><a href="<?php bloginfo('url') ?>/wp-admin/">Dashboard</a></li><?php if ( $user_level >= 1 ) : ?><li><a href="<?php bloginfo('url') ?>/wp-admin/post-new.php">Write an article</a></li><?php endif; ?><li><a href="<?php bloginfo('url') ?>/wp-admin/profile.php">Profile and personal options</a></li><li><a href="<?php bloginfo('url') ?>/wp-login.php?action=logout&amp;redirect_to=<?php echourlencode($_SERVER['REQUEST_URI']) ?>">Logout</a></li><?phpif (is_single()) {?><li><a href="<?php bloginfo('wpurl');?>/wp-admin/edit.php?p=<?php the_ID(); ?>">Edit Post</a></li><?php } ?></ul></li></ul><?php endif; ?></li> Once you are done, save the file. The allowed users can now go to their dashboard, edit their profile, or write a new post directly from the blog. How it works As mentioned earlier, this code works in the same way as the code that was used to create a login form in the sidebar. After you've made sure that the $user_ID variable isn't null, you work towards displaying the options available to the user. It is possible to define what a user can perform according to his role (administrator, author, contributor, subscriber, and so on). We're going to have a look at this in the next recipe. There's more... Now that you have learned how to add a control panel to the blog's sidebar, let's go ahead and try out something new. Adding a login form and a control panel Now that you know how to add a login form and a mini control panel to your blog's sidebar, why not try mixing the two codes? If the user isn't logged in, we'll display the login form. Otherwise, the custom panel will be shown to the user. The code below works in the same way as the two that we studied previously. Add the following code to the sidebar.php file of your theme: <li><?php global $user_ID, $user_identity, $user_level ?><?php if ( $user_ID ) : ?><h2>Control panel</h2><ul><li>Identified as <strong><?php echo $user_identity ?></strong>.<ul><li><a href="<?php bloginfo('url') ?>/wp-admin/">Dashboard</a></li><?php if ( $user_level >= 1 ) : ?><li><a href="<?php bloginfo('url') ?>/wp-admin/post-new.php">Write an article</a></li><?php endif; ?><li><a href="<?php bloginfo('url') ?>/wp-admin/profile.php">Profile and personal options</a></li><li><a href="<?php bloginfo('url') ?>/wp-login.php?action=logout&amp;redirect_to=<?php echo urlencode($_SERVER['REQUEST_URI']) ?>">Logout</a></li><?phpif (is_single()) {?><li><a href="<?php bloginfo('wpurl');?>/wp-admin/edit.php?p=<?php the_ID(); ?>">Edit Post</a></li><?php } ?></ul></li></ul><?php elseif ( get_option('users_can_register') ) : ?><h2>Identification</h2><ul><li><form action="<?php bloginfo('url') ?>/wp-login.php"method="post"><p><label for="log"><input type="text" name="log" id="log" value="<?php echo wp_specialchars(stripslashes($user_login), 1)?>" size="22" /> User</label><br /><label for="pwd"><input type="password" name="pwd" id="pwd"size="22" /> Password</label><br /><input type="submit" name="submit" value="Login"class="button" /><label for="rememberme"><input name="rememberme" id="rememberme" type="checkbox" checked="checked"value="forever" /> Remember me</label><br /></p><input type="hidden" name="redirect_to" value="<?php echo$_SERVER['REQUEST_URI']; ?>"/></form></li><li><a href="<?php bloginfo('url') ?>/wp-register.php">Register</a></li><li><a href="<?php bloginfo('url') ?>/wp-login.php?action=lostpassword">Recover password</a></li></ul><?php endif; ?></li> The custom logging form for unregistered users will look similar to the following screenshot: And the control panel for logged in users will look similar to the following screenshot: Configuring author roles Now that you have learned about the different aspects of the user's roles and capabilities, there's probably something that you're finding a little frustrating. By default, you can't configure author roles to fit your blog's needs. For example, a contributor can't upload images. Moreover, by default, you can't change it. Luckily, there's a plugin called Role Manager which allows you to configure author roles in the way that you want. Getting ready The Role Manager plugin can be found at the following link: http://www.im-web-gefunden.de/wordpress-plugins/role-manager/ Download it, unzip it onto your hard drive, and install it as any other WordPress plugin. How to do it Once the Role Manager plugin is installed, log in to your WordPress dashboard and go to Users | Roles. A list of all of the available user roles will be displayed. For each role you can define what the user can do. For example, you can choose to let a contributor upload images. What is even better is that you're not limited to the 5 default user roles that are provided by WordPress. The Role Manager plugin allows you to create new roles, as well as the ability to rename, copy, or delete existing ones. How it works The job of the Role Manager plugin is pretty easy. It simply creates custom roles with the options that you have defined and save it on the WordPress database. There's more... Now that we have configured the author roles, let's learn how to control the author's actions. Controlling what authors can do Even if your blog is powered by multiples authors, it is still your blog. Therefore, you shouldn't allow every author to have the right to edit posts or delete comments. Since version 2.0, WordPress features user roles. User roles are defined as a group of actions that can be accomplished by a specific range of users. For example, the administrator can edit theme files, but the subscribers can't. User roles and their capabilities Here are the 5 predefined roles for WordPress users: Administrator: The administrator is the blog owner. He has unlimited access to all of the administration features such as writing posts, editing his own posts along with the posts from other authors, installing plugins, selecting a new theme, editing themes, and editing plugin files. Editor: The editor can write or publish posts, upload images, edit his own posts, and manage other's posts. Author: The author can write, publish, and edit his own his own posts. He's also allowed to upload images for use in his posts. Contributor: A contributor can write posts but can't publish them himself. Once he has written a post, the post is pending approval from the administrator. The contributor can't upload images either. This role is very good for guest authors on your own blog. Subscriber: A subscriber is a registered user of your blog, but can't write posts. For an exhaustive description of user roles and capabilities, you should read the related page in WordPress Codex: http://codex.wordpress.org/Roles_and_Capabilities. Controlling what users can see in your theme In the previous example, we built a sidebar control panel that allows the user to edit the current post. However, the code doesn't let you control which kind of author is allowed to edit the current post. For now, even if only the users with a sufficient role level will be capable of editing the post, every logged in user can see the related link. The solution to that problem is a built-in WordPress function, called current_user_can(). As an argument, this function takes a string describing the action or the required role level to perform a specific task. For example, the following code will provide a link to edit the current post to the administrators only: <?phpif (current_user_can('level_10')){ ?><a href="<?php bloginfo('wpurl');?>/wp-admin/edit.php?p=<?php the_ID(); ?>">Edit Post</a><?php } ?> The current_user_can() function accepts user_0 to user_10 as a parameter. Here is the conversion table between the role levels and the roles: Suscriber: level_0 Contributor: level_1 Author: level_2 to level_4 Editor: level_5 to level_7 Administrator: level_8 to level_10 The current_user_can() function can also be used with a specific action as a parameter. This is the recommended use, as the level parameter is becoming obsolete. The following example checks if the current user can edit a post he previously published. If yes, then a link to edit the post will be displayed. <?phpif (current_user_can('edit_published_posts')){ ?><a href="<?php bloginfo('wpurl');?>/wp-admin/edit.php?p=<?php the_ID(); ?>">Edit Post</a><?php } ?> Here are all of the arguments that are accepted by the current_user_can() function:      switch_themes      edit_themes      activate_plugins      edit_plugins      edit_users      edit_files      manage_options      moderate_comments      manage_categories      manage_links      upload_files      import      unfiltered_html      edit_posts      edit_others_posts      edit_published_posts      edit_pages      edit_others_pages      edit_published_pages      edit_published_pages      delete_pages      delete_others_pages      delete_published_pages      delete_posts      delete_others_posts      delete_published_posts      delete_private_posts      edit_private_posts      read_private_posts      delete_private_pages      edit_private_pages      read_private_pages      delete_users      create_users      unfiltered_upload      edit_dashboard      update_plugins      delete_plugins Displaying author-related information on posts In a multi-author blog, it's always good for the reader to know the author of the article that they're currently reading. It's even better if they can grab some extra information about the author, such as his website, a short bio, and so on. In this recipe, you'll learn how to edit your single.php theme file to automatically retrieve the author-related information, and display it at the top of the page. Getting ready As we're going to display author information on posts, the first thing to do is to make sure that your contributing authors have entered their biography and other information into the WordPress database. Any author can enter his information by logging in to the WordPress dashboard, and then going to Profile. The blog administrator can edit all of the profiles. The following screenshot shows the WordPress 2.7 profile editor for the authors. How to do it Once you have made sure that your authors have successfully filled their information, you can start coding by carrying out the following steps: Open the file single.php for addition. Paste the following code within the loop: <div id="author-info"><h2>About the author: <?php the_author();?></h2><?php the_author_description(); ?><?php the_author();?>'s website: <a href="<?php the_author_url();?>"><?php the_author_url(); ?></a><br />Other posts by <?php the_author_posts_link(); ?></div><!--/author-info--> Save the file and visit your blog. You will notice that your posts now automatically display the author-related information, as shown in the following screenshot: How it works WordPress provides a dozen of author-related template tags, which are an easy way to retrieve information that is entered by authors in their profile. Note that all of these tags must be used within the loop for them to work. There's more... Here are all the available template tags related to authors:
Read more
  • 0
  • 1
  • 2846

article-image-views-urls-and-generic-views-django-10
Packt
16 Oct 2009
19 min read
Save for later

Views, URLs, and Generic Views in Django 1.0

Packt
16 Oct 2009
19 min read
An overview Views are at the heart of Django and hold most of your application logic. They are nothing more than Python functions that take an HTTP request as input and return an HTTP response or error. A mechanism called the dispatcher identifies an incoming URL against a set of URL patterns and their associated view functions. When a match is found, the associated view is called and the request gets handled. Since many views follow a common strategy of loading an object or list, loading a template, rendering the template, and returning a response, Django offers a way of doing this without writing a view function. These generic views are called from the URL dispatcher and go right to the template. Creating the application Before we start looking at views and URLs, let's create a sample application to experiment with. Since most books and examples use blog models as their demos, let's keep things fresh by making our demo a press release application for a company website. The press release object will have a title, body, published date, and author name. Create the data model In the root directory of your project (in the directory projects/mycompany), create the press application by using the startapp command: $ python manage.py startapp press This will create a press folder in your site. Edit the mycompany/press/models.py file: from django.db import modelsclass PressRelease(models.Model): title = models.CharField(max_length=100) body = models.TextField() pub_date = models.DateTimeField() author = models.CharField(max_length=100) def __unicode__(self): return self.title Create the admin file To take advantage of the automatic admin interface that Django gives us, we need to create a file called an admin file. Create a file called admin.py in the mycompany/press directory, adding these lines: from django.contrib import adminfrom mycompany.press.models import PressReleaseadmin.site.register(PressRelease) If you've used Django before version 1.0, this step is new. The admin configuration directives were taken out of the model and put into their own files starting in version 1.0. Add the press and admin applications to your INSTALLED_APPS variable in the settings.py file: INSTALLED_APPS = ( 'django.contrib.auth', 'django.contrib.admin', 'django.contrib.contenttypes', 'djan?go.contrib.sessions', 'django.contrib.sites', 'mycompany.press',) In the root directory of your project, run the syncdb command to add the new models to the database: $ python manage.py syncdb Because we have Django's authentication system listed as one of our installed applications, the initial syncdb process will ask us if we want to create a superuser. Go ahead and create a superuser account; you will be using it later to access the admin site. Configure the URLs Finally, edit the mycompany/urls.py file: from django.conf.urls.defaults import *from django.contrib import adminadmin.autodiscover()urlpatterns = patterns('', (r'^admin/(.*)', admin.site.root),) Add data in the admin application By adding django.contrib.admin to our INSTALLED_APPS setting and creating a URL mapping for it, we can access the admin site by browsing to http://localhost:8000/admin/. Go into the admin app and add two or three press releases so that we have some sample data to work with: Mapping URLs to views When Django accepts an incoming request, one of the first things it does is that it looks at the URL and tries to match it against a group of URL patterns. In order to identify patterns, Django uses regular expressions to see if the URLs follow a known format. Consider these URLs: http://localhost:8000/press/detail/1/ http://localhost:8000/press/detail/2/ These URLs appear to follow a pattern that they start with press/detail/ and end with a number that represents the ID of a press release. (Recall that we don't work with the domain name portion of the URL. Django takes care of this automatically for us and just sends us everything that follows the domain name.) With this pattern, we can add a new line to our mycompany/urls.py file: from django.conf.urls.defaults import *from django.contrib import adminadmin.autodiscover()urlpatterns = patterns('', (r'^admin/(.*)', admin.site.root), (r'^press/detail/d+/$', 'mycompany.press.views.detail'),) If you're not familiar with Python's regular expressions, this new line may look a bit wonky. This is the most important part: r'^press/detail/d+/$' It reads like this: "A string that starts with press/detail/ and ends with one or more digits followed by a slash". The second segment of the new line is the view function that will get called when an incoming URL matches this pattern. In this case, it will be a function called detail in the mycompany/press/views.py file. There's only one problem with this pattern—it recognizes that a number will be at the end of the URL, but doesn't do anything to pass that number to the view when it's called. We can use a Python regular expression group to capture that number: urlpatterns = patterns('', (r'^admin/', include('django.contrib.admin.urls')), (r'^press/detail/(?P<pid>d+)/$', 'mycompany.press.views.detail'),) This grouping syntax looks really funky, but it's easy to understand once you've seen it a few times. (?P ) is the Python syntax for a named group, which allows the regular expression to save the piece that matched, and put a label on it so that we can call it later. The <pid> part is where we assign the label of pid to the ID of the press release that was sent with the URL. In the case of this URL, the named group pid will be equal to 2: http://localhost:8000/press/detail/2/ Any named groups that we get from a URL are passed as arguments to our view function. In this example, our detail function in press/views.py will have a method signature like this: def detail(request, pid): p = PressRelease.object.get(id=pid) .. There are two keyword arguments to the detail function, request and pid. (Django automatically passes the keyword request, which we'll explore a little later.) Because we used a named group in the URL configuration to capture the press release ID, it's passed to our detail function as pid. You can use multiple named groups in your URL patterns to capture multiple pieces of information and pass them to your functions. Note: URL configurations and patterns are usually referred to as URLConf. Handling unmatched URL patterns URLs are matched up with view functions when they match patterns, but what happens when a match isn't found? This URL wouldn't match the patterns we created because it doesn't end in a number: http://localhost:8000/press/detail/abc/ In this case, the URL dispatcher wouldn't match against our pattern and would keep trying other patterns until a match is found. If no match is found, a 404 error is raised. If you have debug set to true (DEBUG=True) in your settings file, you'll see an error message like this: Splitting up the URL configurations We created the URL configurations for the press application in the mycompany/urls.py file. While this is perfectly acceptable, sticking all the configurations into the main urls.py file can get unwieldy for large projects with many applications. It also isn't very modular if we want to share applications with others or use applications that other people distribute. Instead of writing the press release configuration in our main mycompany/urls.py file, let's create a new file at mycompany/press/urls.py: from django.conf.urls.defaults import *urlpatterns = patterns('', (r'^detail/(?P<pid>d+)/$', 'press.views.detail'),) This looks very similar to what we already have, but note that we've dropped press from the beginning of the regular expression. This line will match URLs that start with detail. Open your mycompany/urls.py file and edit the highlighted line: from django.conf.urls.defaults import *from django.contrib import adminadmin.autodiscover()urlpatterns = patterns('', (r'^admin/(.*)', admin.site.root), (r'^press/', include('mycompany.press.urls')),) We've changed the regular expression portion to match URLs that start with press/. If one is found, Django will hop over to the press/urls.py file to try to match the rest of the URL (without the press/ prefix). With this setup, we are telling Django that any URLs that start with press will be handled in a separate urls.py file in the press directory. Creating views Now that we're matching a URL to a view and passing it information, we can look at how a view is structured. Views have two rules you must follow: The view must accept the request object as its first argument. The view must return an HTTP response or an exception. Beyond this, just remember that a view is a standard Python function and you can do just about anything in it that you can do in a Python program. Accepting the request object Our first rule for views states that a view must accept the request object as its first argument. What is this request object? Django automatically creates the request object when a page is requested. It contains data about the incoming HTTP request such as the requestor's IP address, user agent, request method, cookies, GET parameters, POST parameters, and so on. Everything you should need to know about an incoming request will be found in this object. When you build your view functions, always specify request as the first keyword argument: def detail(request): # Python code here If you forget to add request as the first parameter, you'll know quickly because your view will fail to load with some kind of error message about the arguments (the exact error depends on what other keyword arguments you might be using). Responding with an HTTP response The second rule for views is that a view must return an HTTP response or an exception. Let's start by talking about what an HTTP response is. In order for a browser to understand how to render a web page, it looks at some special hidden information called headers, which is sent by the server along with the content or document being requested. These headers tell the browser information such as what kind of web server is sending the response, which version of the HTTP protocol is being used, how big the content is, and what kind of content is being sent. Luckily, we don't have to worry about most of this because the web server and Django take care of it for us. All we have to do is make sure we send the response out of our view using the HttpResponse method. In your mycompany/press/views.py file, add the following lines: from django.http import HttpResponsedef detail(request, pid): return HttpResponse('This is just a test.') Point your browser to http://localhost:8000/press/detail/1/. Here's what it should look like: Obviously, our views are going to be more complicated than this one, but it illustrates how simple they can be. Responding with an exception The second part of our rule said that the view can respond with an exception instead of an HTTP response. When Django encounters an error during the processing of a view, we usually want to return a friendly error message to the user to let them know something went wrong (as opposed to just sending back a blank screen). Usually, these error messages are in the form of 404 or 500 Error pages. 404 errors are also known as page not found errors. Anyone who has spent time surfing the Web has undoubtedly encountered a 404 Error page when clicking an old link that is no longer valid. In traditional HTML publishing, 404 errors popped up when the user requested a filename that wasn't found on the server (that's where the "page" in "page not found" comes from). With Django, we don't have URLs that represent filenames on the server, but we still return a 404 error when the user is looking for a resource that does not exist. Django makes it easy to return a 404 page by returning the error using the HttpResponseNotFound function: from django.http import HttpResponseNotFounddef detail(request, pid): return HttpResponseNotFound('Page Not Found') Similarly, requests that cause errors on the server are usually referred to as 500 errors. (500 is the standard HTTP response code for a server error.) Django also makes it easy to serve a 500 error: from django.http import HttpResponseServerErrordef detail(request, pid): return HttpResponseServerError('An Error Has Occurred.') Putting the views together Now that we know how a view works and what it needs to do, let's write the real view to work with our sample application. Building the basic view In your mycompany/press/views.py file, replace any contents with the following lines: from django.http import HttpResponsefrom django.http import HttpResponseNotFoundfrom mycompany.press.models import PressReleasedef detail(request, pid): ''' Accepts a press release ID and returns the detail page ''' try: p = PressRelease.objects.get(id=pid) return HttpResponse(p.title) except PressRelease.DoesNotExist: return HttpResponseNotFound('Press Release Not Found') If you'd like to test it out, point your browser to http://localhost:8000/press/detail/1/. You should see the title of your press release. Change the number at the end of the press release to an ID that doesn't exist (such as 99) and you should get a Page Not Found error. This view doesn't return a very pretty output, but it follows the rule that the view must serve an HTTP response or an error/exception. The try/except error handling to make sure the press release exists is kind of ugly. Luckily, Django gives us a more elegant way of handling it. Cleaning up the error handling Instead of putting a try/except block around the object lookup, Django has a get_object_or_404 method that will automatically raise an error if the object is not found. Change the highlighted lines in your mycompany/press/views.py file: from django.http import HttpResponsefrom django.shortcuts import get_object_or_404from mycompany.press.models import PressReleasedef detail(request, pid): ''' Accepts a press release ID and returns the detail page ''' p = get_object_or_404(PressRelease, id=pid) return HttpResponse(p.title) That's a much cleaner way of doing things! Note: If you're getting a list instead of an object, Django has a get_list_or_404 method that you can use. We'll see this in a few pages. Adding the template files The last thing we need to do is add a way to load up the response with the output of a rendered template. We're going to load a template file, replace placeholders in that file with our data (called "rendering" the template), and then return the contents of the template as a string as an HTTP response. We create a templates directory at mycompany/templates, and configured the settings.py file to tell Django where to find it: TEMPLATE_DIRS = ( '/projects/mycompany/templates/',) Verify that you have configured your project this way before continuing. With this setting in place, we can load templates relative to this path. Create a directory under the mycompany/templates directory called press. (It's common practice to use subdirectories to group template files by the application they are associated with.) Create a new file at mycompany/templates/press/detail.html and add these lines: <html><head><title>{{ press.title }}</title></head><body><h1>{{ press.title }}</h1><p>Author: {{ press.author }}<br/>Date: {{ press.pub_date }}<br/></p><p>{{ press.body }}</p></body></html> This simple template file has placeholders for our title, author, pub_date, and body fields. When the template is rendered, these placeholders will be replaced with their respective values. Now that we have a template, we can tell the view to use it. Adding the template to the view In our mycompany/press/views.py file, let's add a few lines to load our template. Replace the contents of your file with these lines: from django.http import HttpResponsefrom django.shortcuts import get_object_or_404from django.template import loader, Contextfrom mycompany.press.models import PressReleasedef detail(request, pid): ''' Accepts a press release ID and returns the detail page ''' p = get_object_or_404(PressRelease, id=1) t = loader.get_template('press/detail.html') c = Context({'press': p}) rendered_template = t.render(c) return HttpResponse(rendered_template) In the function, we're retrieving the press/detail.html template file and creating a special data object called Context. So for now, just understand that it passes data to the template so that it can be rendered. The context object in this example passes our press release object to the template in a variable called press. Our template gets rendered into a string called rendered_template that is sent back to the browser via HttpResponse the same way we sent back simple lines of text in previous examples. The rendered_template variable was used for clarity. You can omit it and just return the response like this: def detail(request, pid): ''' Accepts a press release ID and returns the detail page ''' p = get_object_or_404(PressRelease, id=1) t = loader.get_template('press/detail.html') c = Context({'press': p}) return HttpResponse(t.render(c)) Point your browser to the URL http://localhost:8000/detail/1/. You should see something like this depending on what you entered earlier into the admin site as sample data: Creating the list view and template In addition to displaying the detail for a specific press release, we'll also need a way to display a list of press releases. The steps to add this will be very similar to what we just did to add our detail view. In your mycompany/press/views.py file, add the highlighted lines: from django.http import HttpResponsefrom django.shortcuts import get_object_or_404from django.shortcuts import get_list_or_404from django.template import loader, Contextfrom mycompany.press.models import PressReleasedef detail(request, pid): ''' Accepts a press release ID and returns the detail page ''' p = get_object_or_404(PressRelease, id=1) t = loader.get_template('press/detail.html') c = Context({'press': p}) return HttpResponse(t.render(c))def press_list(request): ''' Returns a list of press releases ''' pl = get_list_or_404(PressRelease) t = loader.get_template('press/list.html') c = Context({'press_list': pl}) return HttpResponse(t.render(c)) In your mycompany/press/urls.py file, add the highlighted line: from django.conf.urls.defaults import *urlpatterns = patterns('', (r'detail/(?P<pid>d+)/$','mycompany.press.views.detail'), (r'list/$', 'mycompany.press.views.press_list'),) Any incoming request starting with press/ will be sent to our press/urls.py file. If the remaining part of the URL is list/, it will be handled by the press_list function in our press/views.py file. If the remaining part is detail/<number> (such as detail/1 or detail/2), it will be handled by the detail function. Finally, create a new file at mycompany/templates/press/list.html: <html><head><title>Press Releases</title></head><body><h1>Press Releases</h1><ul>{% for press in press_list %}<li><a href="/press/detail/{{ press.id }}/">{{ press.title }}</a></li>{% endfor %}</ul></body></html> Point your browser to the URL http://localhost:8000/press/list/. You should see something like this, depending on what you entered earlier into the admin site: Using generic views to shorten development time What we've done so far in this article is pretty standard for web application development: We created a view to load an object by its ID. We created a view to load a list of objects. We retrieved our object using the data sent in from the URL or retrieved a list of objects. We loaded a template file. We rendered the template. We returned an HTTP response. Because these actions are so common, Django has a way to cut out the whole step of writing a view, called generic views. Generic views are called from the URL configuration file, which allows you to go right from the URL pattern to your template. Generic views come in a few types: Simple List/detail Date-based Create/update/delete We won't be covering the date-based or create/update/delete generic views. But after reading this article, you'll be well-prepared to read about them in the online documentation. Simple generic views The two simple generic views that handle loading of a template don't require any data lookup (going directly to a template) and redirecting from one URL to another. Loading a template directly If you just need to load and render a template when a URL is requested, you can use the direct_to_template generic view. For example, let's build a robots exclusion file (aka a robots.txt file) that search engine spiders will request at http://localhost:8000/robots.txt. (Search engines wouldn't index pages on a localhost domain, but pretend for this example that they would.) Since the file is rarely changed after being created, you may not want the overhead of a database lookup to serve it, so you just want to render a template when the URL is requested. Create a new file at mycompany/templates/robots.txt and add these lines: User-agent: *Disallow: /admin This very simple example will prevent spiders from trying to index your admin path (visit robotstxt.org for more info on how exclusion files work). In your mycompany/urls.py file, add the highlighted lines: from django.conf.urls.defaults import *from django.contrib import adminadmin.autodiscover()urlpatterns = patterns('', (r'^admin/(.*)', admin.site.root), (r'^press/', include('mycompany.press.urls')), (r'^robots.txt$', 'django.views.generic.simple.direct_to_template', 'template': 'robots.txt'}), ) Point your browser to the URL http://localhost:8000/robots.txt/. You'll get a response that looks like this:
Read more
  • 0
  • 0
  • 4948
Unlock access to the largest independent learning library in Tech for FREE!
Get unlimited access to 7500+ expert-authored eBooks and video courses covering every tech area you can think of.
Renews at $19.99/month. Cancel anytime
article-image-building-facebook-application-part-2
Packt
16 Oct 2009
11 min read
Save for later

Building a Facebook Application: Part 2

Packt
16 Oct 2009
11 min read
Mock AJAX and your Facebook profile I'm sure that you've heard of AJAX (Asynchronous JavaScript and XML) with which you can build interactive web pages. Well, Facebook has Mock AJAX, and with this you can create interactive elements within a profile page. Mock AJAX has three attributes that you need to be aware of: clickwriteform: The form to be used to process any data. clickwriteid: The id of a component to be used to display our data. clickwriteurl: The URL of the application that will process the data. When using Mock AJAX, our application must do two things: Return the output of any processed data (and we can do that by using either echo or print). Define a form with which we'll enter any data, and a div to receive the processed data Using a form on your profile Since we want to make our application more interactive, one simple way is to add a form. So, for our first example we can add a function (or in this case a set of functions) to appinclude.php that will create a form containing a simple combo-box: function country_combo () {/*You use this function to display a combo-box containing a list of countries. It's in its own function so that we can use it in other forms without having to add any extra code*/$country_combo = <<<EndOfText<select name=sel_country><option>England</option><option>India</option></select>EndOfText;return $country_combo;}function country_form () {/*Like country_combo-box we can use this form where ever needed because we've encapsulated it in its own function */global $appcallbackurl;$country_form = "<form>";$country_form .= country_combo ();$country_form .= <<<EndOfText<input type="submit" clickrewriteurl="$appcallbackurl" clickrewriteid="info_display" value="View Country"/><div id="info_display" style="border-style: solid; border-color: black; border-width: 1px; padding: 5px;">No country selected</div></form>EndOfText;return $country_form;}function display_simple_form () {/*This function displays the country form with a nice subtitle (on the Profile page)*/global $facebook, $_REQUEST;#Return any processed dataif (isset($_REQUEST['sel_country'])) { echo $_REQUEST['sel_country'] . " selected"; exit;}#Define the form and the div$fbml_text = <<<EndOfText<fb:subtitle><fb:name useyou=false uid=$user firstnameonly=true possessive=true> </fb:name> Suspect List</fb:subtitle>EndOfText;$fbml_text .= country_form ();$facebook->api_client->profile_setFBML($fbml_text, $user);echo $fbml_text;} And, of course, you'll need to edit index.php: display_simple_form (); You'll notice from the code that we need to create a div with the id info_display, and that this is what we use for the clickrewriteid of the submit button. You'll also notice that we're using $appcallbackurl for the clickrewriteurl ($appcallbackurl is defined in appinclude.php). Now, it's just a matter of viewing the new FMBL (by clicking on the application URL in the left-navigation panel): If you select a country, and then click on View Country, you'll see: I'm sure that you can see where we're going with this. The next stage is to incorporate this form into our Suspect Tracker application. And the great thing now is that because of the functions that we've already added to appinclude.php, this is now a very easy job: function first_suspect_tracker () {global $facebook, $_REQUEST;if (isset($_REQUEST['sel_country'])) { $friend_details = get_friends_details_ by_country ($_REQUEST['sel_country']); foreach ($friend_details as $friend) { $div_text .= "<fb:name uid=" . $friend['uid'] . " firstnameonly=false></fb:name>, "; } echo $div_text; exit;}$fbml_text .= country_form ();$facebook->api_client->profile_setFBML($fbml_text, $user);$facebook->redirect($facebook->get_facebook_url() . '/profile.php');} You may also want to change the country_form function, so that the submit button reads View Suspects. And, of course, we'll also need to update index.php. Just to call our new function: <?phprequire_once 'appinclude.php';first_suspect_tracker ();?> This time, we'll see the list of friends in the selected country: or: OK, I know what you're thinking, this is fine if all of your friends are in England and India, but what if they're not? And you don't want to enter the list of countries manually, do you? And what happens if someone from a country not in the list becomes your friend? Obviously, the answer to all of these questions is to create the combo-box dynamically. Creating a dynamic combo-box I'm sure that from what we've done so far, you can work out how to extract a list of countries from Facebook: function country_list_sql () {/*We're going to be using this piece of SQL quite often so it deserves its own function*/global $user;$country_list_sql = <<<EndSQLSELECT hometown_location.countryFROM userWHERE uid IN (SELECT uid1FROM friendWHERE uid2=$user)EndSQL;return $country_list_sql;}function full_country_list () {/*With the SQL in a separate function this one is very short and simple*/global $facebook;$sql = country_list_sql ();$full_country_list = $facebook-> api_client->fql_query($sql);print_r ($full_country_list);} However, from the output, you can see that there's a problem with the data: If you look through the contents of the array, you'll notice that some of the countries are listed more than once—you can see this even more clearly if we simulate building the combo-box: function options_country_list () {global $facebook;$sql = country_list_sql ();$country_list = $facebook->api_client->fql_query($sql);foreach ($country_list as $country){ echo "option:" . $country['hometown_location']['country'] ."<br>";}} From which, we'd get the output: This is obviously not what we want in the combo-box. Fortunately, we can solve the problem by making use of the array_unique method, and we can also order the list by using the sort function: function filtered_country_list () {global $facebook;$sql = country_list_sql ();$country_list = $facebook->api_client->fql_query($sql);$combo_full = array();foreach ($country_list as $country){ array_push($combo_full, $country['hometown_location']['country']);}$combo_list = array_unique($combo_full);sort($combo_list);foreach ($combo_list as $combo){ echo "option:" . $combo ."<br>";}} And now, we can produce a usable combo-box: Once we've added our code to include the dynamic combo-box, we've got the workings for a complete application, and all we have to do is update the country_combo function: function country_combo () {/*The function now produces a combo-box derived from the friends' countries */global $facebook;$country_combo = "<select name=sel_country>";$sql = country_list_sql ();$country_list = $facebook->api_client->fql_query($sql);$combo_full = array();foreach ($country_list as $country){ array_push($combo_full, $country['hometown_location']['country']);}$combo_list = array_unique($combo_full);sort($combo_list);foreach ($combo_list as $combo){ $country_combo .= "<option>" . $combo ."</option>";}$country_combo .= "</select>";return $country_combo;} Of course, you'll need to reload the application via the left-hand navigation panel for the result: Limiting access to the form You may have spotted a little fly in the ointment at this point. Anyone who can view your profile will also be able to access your form and you may not want that (if they want a form of their own they should install the application!). However, FBML has a number of if (then) else statements, and one of them is <fb:if-is-own-profile>: <?phprequire_once 'appinclude.php';$fbml_text = <<<EndOfText<fb:if-is-own-profile>Hi <fb:name useyou=false uid=$user firstnameonly=true></fb:name>, welcome to your Facebook Profile page.<fb:else>Sorry, but this is not your Facebook Profile page - it belongs to <fb:name useyou=false uid=$user firstnameonly=false> </fb:name>,</fb:else></fb:if-is-own-profile>EndOfText;$facebook->api_client->profile_setFBML($fbml_text, $user);echo "Profile updated";?> So, in this example, if you were logged on to Facebook, you'd see the following on your profile page: But anyone else viewing your profile page would see: And remember that the FBML is cached when you run: $facebook->api_client->profile_setFBML($fbml_text, $user); Also, don't forget, it is not dynamic that is it's not run every time that you view your profile page. You couldn't, for example, produce the following for a user called Fred Bloggs: Sorry Fred, but this is not Your Facebook Profile page - it belongs to Mark Bain That said, you are now able to alter what's seen on the screen, according to who is logged on. Storing data—keeping files on your server From what we've looked at so far, you already know that you not only have, but need, files stored on your server (the API libraries and your application files). However, there are other instances when it is useful to store files there. Storing FBML on your server In all of the examples that we've worked on so far, you've seen how to use FBML mixed into your code. However, you may be wondering if it's possible to separate the two. After all, much of the FBML is static—the only reason that we include it in the code is so that we can produce an output. As well as there may be times when you want to change the FBML, but you don't want to have to change your code every time you do that (working on the principle that the more times you edit the code the more opportunity there is to mess it up). And, of course, there is a simple solution. Let's look at a typical form: <form><div id="info_display" style="border-style: solid; border-color: black; border-width: 1px; padding: 5px;"></div><input name=input_text><input type="submit" clickrewriteurl="http://213.123.183.16/f8/penguin_pi/" clickrewriteid="info_display" value="Write Result"></form> Rather than enclosing this in $fbml_text = <<<EndOfText ... EndOfText; as we have done before, you can save the FBML into a file on your server, in a subdirectory of your application. For example /www/htdocs/f8/penguin_pi/fbml/form_input_text.fbml. "Aha" I hear your say, "won't this invalidate the caching of FBML, and cause Facebook to access my server more often than it needs?" Well, no, it won't. It's just that we need to tell Facebook to update the cache from our FBML file. So, first we need to inform FBML that some external text needs to be included, by making use of the <fb:ref> tag, and then we need to tell Facebook to update the cache by using the fbml_refreshRefUrl method: function form_from_server () {global $facebook, $_REQUEST, $appcallbackurl, $user;$fbml_file = $appcallbackurl . "fbml/form_input_text.fbml";if (isset($_REQUEST['input_text'])) { echo $_REQUEST['input_text']; exit;}$fbml_text .= "<fb:ref url='" . $fbml_file . "' />";$facebook->api_client->profile_setFBML($fbml_text, $user);$facebook->api_client->fbml_refreshRefUrl($fbml_file);echo $fbml_text;} As far as your users are concerned, there is no difference. They'll just see another form on their profile page: Even if your users don't appreciate this leap forward, it will make a big difference to your coding—you're now able to isolate any static FBML from your PHP (if you want). And now, we can turn our attention to one of the key advantages of having your own server—your data. Storing data on your server So far, we've concentrated on how to extract data from Facebook and display it on the profile page. You've seen, for example, how to list all of your friends from a given country. However, that's not how Pygoscelis' list would work in reality. In reality, you should be able to select one of your friends and add them to your suspect list. We will, therefore, spend just a little time on looking at creating and using our own data. We're going to be saving our data in files, and so your first job must be to create a directory in which to save those files. Your new directory needs to be a subdirectory of the one containing your application. So, for example, on my Linux server I would do: cd /www/htdocs/f8/penguin_pi       #Move to the application directory mkdir data #Create a new directory chgrp www-data data                          #Change the group of the directory chmod g+w data                                  #Ensure that the group can write to data
Read more
  • 0
  • 0
  • 2555

article-image-extending-document-management-alfresco-3
Packt
16 Oct 2009
5 min read
Save for later

Extending Document Management in Alfresco 3

Packt
16 Oct 2009
5 min read
Microsoft Office 2003 add-ins For Microsoft Windows users, a natural way of working with the files is by using the Microsoft Office tools. It would be a tedious job for Content Managers to have to search and locate the documents using an Alfresco web client, copy them onto their local desktop, edit them, upload them to Alfresco, and secure them. How about having all of the features mentioned above in your choice of editor itself? Alfresco provides Office add-ins for MS Word 2003, MS Excel 2003, and MS PowerPoint 2003, to allow them to manage the content directly from those tools. This improves the productivity of Content Managers. Support for Microsoft Office 2007 Although the Alfresco add-ins were developed for Microsoft Office 2003, they are also compatible with Microsoft Office 2007. If you are using Microsoft Office 2007 on Windows Vista, then the add-in is not effective, as it provides read-only access to the repository. Unfortunately, this is a known problem with Vista, as Microsoft has rewritten the WebDAV parts of Vista. You may consider the workarounds that are provided at the following URL: http://blogs.msdn.com/sharepoint/archive/2007/10/19/known-issue-office-2007-on-windows-vista-prompts-for-user-credentials-when-opening-documents-in-a-sharepoint-2007-site.aspx Installation Download the Alfresco office add-ins (ZIP file) from the source forge web site, by visiting the following URL: http://sourceforge.net/project/showfiles.php?group_id=143373&package_id=237030 An individual installer (for Microsoft Word, Excel, and Power Point), as well as a combined installer, is available for download. Select an appropriate add-into download. Unzip the ZIP file and run the Setup.exe file contained within it. The set-up program will download the components that are needed, from the Microsoft web site. Once the set-up is complete, you can open the Office tool and use the add-in. For example, for MS Word 2003, you will notice a new button named Alfresco. For MS Word 2007, you will notice the add-in, as shown in the following screenshot: Configuration Click on the Alfresco button to open the add-in window. You need to configure the add-in, by clicking on the link provided at the bottom of the add-in window. Provide the URL details for the web client, WebDAV, and CIFS, as shown in the upcoming screenshot. No matter how you access the repository, you will still have to go through Alfresco's security rules. Provide the Userid and password for the purpose of authentication. The access to the Alfresco repository will be based on the authorization of the user. Click on the Save Settings button to go the main screen. If you have more than one Alfresco server to connect to, then you might have to manually change the settings as needed. Currently, there is no facility for storing the settings for more than one Alfresco server. Features of MS Word add-in The Alfresco add-in allows you to carry out the following activities directly from Microsoft Word. Refer to the following screenshot for more details: My Alfresco: Displays the My Alfresco dashlets Browse Spaces: Browses the entire repository for spaces and files. Search: Searches the repository for keywords. View Details: Views the details of the selected document. Workflow: Starts workflow for the active document. Tags: Allows you to add tags to the document. Transform to PDF: Transforms the selected MS Word document into PDF. Insert into Word: Inserts the selected document into Microsoft Word for editing. Save to Alfresco: Saves the current document to the current space. If the document has not been given a filename yet, then a pop-up panel will prompt you for one. Editing a file in Word To edit a file in Microsoft Word, double-click on the file name. The file is opened directly for editing. The MS Word file is locked for others, while it is being edited by you, as shown in the upcoming screenshot. You can perform all of the Alfresco repository activities, such as adding new tags and initiating a workflow approval process. Saving the file in Microsoft Word will directly save it in the Alfresco repository. If auto version is enabled, then it will be versioned automatically. When you close the file in MS Word, or exit from MS Word, the file will be unlocked in the repository. Recovering deleted content When you delete an item (either content or space) in Alfresco, the item is not deleted from the server, but is moved to a temporary store called Archive Space Store. This gives you a chance to recover items that were deleted. Deleted items will be kept in the temporary store forever, until you decide to either recover or purge them. These features are available to administrators through the Manage Deleted Items action. To test these features, log in as an administrator, create a couple of dummy files in any space, and then delete them. Click on the User Profile Icon  option, located above the menu item, and then click on the Manage Deleted Items button. The Manage Deleted Items pane appears, as shown in the following screenshot: You can list all of the deleted content by clicking on the Show All button, as highlighted in the preceding screenshot. You can also search for deleted items by name, by content, by date, or by the person who deleted it, by using the search options provided. Select the item that you previously deleted, and then click on the Recover Listed Items icon, as shown in the preceding screenshot. You will notice that the item is recovered to the original space. When an item is recovered, it is removed from the archive space store and moved to the original space from which it was deleted. Purged items are deleted forever and cannot be recovered. Because the deleted items will otherwise be in the temporary store forever, it is a good practice to purge them periodically. It is also recommended that you take regular backups of your data.  
Read more
  • 0
  • 0
  • 2259

article-image-ajaxdynamic-content-and-interactive-forms-joomla
Packt
16 Oct 2009
13 min read
Save for later

AJAX/Dynamic Content and Interactive Forms in Joomla!

Packt
16 Oct 2009
13 min read
AJAX: an acronym that Jesse James Garret of AdaptivePath.com came up with in 2005. Just a few short years later, it seems like every site has a "taste" of AJAX in it. If you're totally new to AJAX, I'll just point out that, at its core, AJAX is nothing very scary or horrendous. AJAX isn't even a new technology or language. Essentially, AJAX stands for: Asynchronous JavaScript and XML, and it is the technique of using JavaScript and XML to send and receive data between a web browser and a web server. The biggest advantage this technique has is that you can dynamically update a piece of content on your web page or web form with data from the server (preferably formatted in XML), without forcing the entire page to reload. The implementation of this technique has made it obvious to many web developers that they can start making advanced web applications (sometimes called RIAs—Rich Interface Applications) that work and feel more like software applications than web pages. Keep in mind that the word AJAX is starting to have its own meaning (as you'll also note its occasional use here as well as all over the Web as a proper noun rather than an all-cap acronym). For example, a Microsoft web developer may use VBScript instead of JavaScript to serve up Microsoft Access database data that is transformed into JSON (not XML) using a .NET server-side script. Today, that guy's site would still be considered an AJAX site rather than an "AVAJ" site (yep, AJAX just sounds cooler). In fact, it's getting to the point where just about anything on a web site (that isn't in Flash) that slides, moves, fades, or pops up without rendering a new browser window is considered an "Ajaxy" site. In truth, a large portion of these sites don't truly qualify as using AJAX, they're just using straight-up JavaScripting. Generally, if you use cool JavaScripts in your Joomla! site, it will probably be considered Ajaxy, despite not being asynchronous or using any XML. Want more info on this AJAX business? The w3schools site has an excellent introduction to AJAX, explaining it in straightforward simple terms. They even have a couple of great tutorials that are fun and easy to accomplish even if you only have a little HTML, JavaScript and server-side script (PHP or ASP) experience (no XML experience required): http://w3schools.com/ajax/. Preparing for dynamic content and interactive forms Gone are the days of clicking, submitting, and waiting for the next page to load, or manually compiling your own content from all your various online identities to post in your site. A web page using AJAX techniques (if applied properly) will give the user a smoother and leaner experience. Click on a drop-down option and check-box menus underneath are immediately updated with the relevant choices—no submitting, no waiting. Complicated forms that, in the past, took two or three screens to process can be reduced into one convenient screen by implementing the form with AJAX. As wonderful as this all sounds, I must again offer a quick disclaimer: I understand that, like with drop-down menus and Flash, you may want AJAX to be in your site, or your clients are demanding that AJAX be in their sites. Just keep in mind, AJAX techniques are best used in situations where they truly benefit a user's experience of a page; for example, being able to painlessly add relevant content via an extension or cutting a lengthy web process form down from three pages to one. In a nutshell, using an AJAX technique simply to say your site is an AJAX site is probably not a good idea. You should be aware that, if not implemented properly, some uses of AJAX can compromise the security of your site. You may inadvertently end up disabling key web browser features (such as back buttons or the history manager). Then there's all the basic usability and accessibility issues that JavaScript in general can bring to a site. Some screen readers may not be able to read a new screen area that's been generated by JavaScript. If you cater to users who rely on tabbing through content, navigation may be compromised once new content is updated. There are also interface design problems that AJAX brings to the table (and Flash developers can commiserate). Many times, in trying to limit screen real estate and simplify a process, developers actually end up creating a form or interface that is unnecessarily complex and confusing, especially when your user is expecting a web page to, well, act like a normal web page. Remember to check in with Don't Make Me Think: This is the Steve Krug book I recommend for help with any interface usability questions you may run into.Really interested in taking on AJAX? For you programmers, I highly recommend "AJAX and PHP: Building Responsive Web Applications", Cristian Darie, Bogdan Brinzarea, Filip Chereches-Tosa, and Mihai Bucica, Packt Publishing. In it, you'll learn the ins and outs of AJAX development, including handling security issues. You'll also do some very cool stuff, such as make your own Google-style auto-suggest form and a drag-and-drop sortable list (and that's just two of the many fun things to learn in the book). So, that said, you're now all equally warned and armed with all the knowledgeable resources I can think to throw at you. Let's get to it: how exactly do you go about getting something Ajaxy into your Joomla! site? Joomla! extensions Keep in mind, extensions are not part of your template. They are additional files with Joomla!-compatible PHP code, which are installed separately into their own directories in your Joomla! 1.5 installation. Once installed, they are available to be used with any template that is also installed in your Joomla! installation. Even though these are not part of your template, you might have to prepare your template to be fully compatible with them. Some extensions may have their own stylesheets, which are installed in their extension directory. Once you've installed an extension, you may want to go into your own template's stylesheet so that it nicely displays XHTML objects and content that the extension may output into your site. Extensions are any component, module or plugin that you install into your Joomla! 1.5 installation. Components control content that displays in the main type="component" jdoc tag in your template. Note that components may also have module settings and the ability to display content in assigned module positions. The poll component is a good example of a component that also has module settings. Modules are usually smaller and lighter and only display in module positions. Plugins generally help you out more on the backend of your site, say to switch WYSIWYG editors or with enabling OpenID logins, but as we'll see, some plugins can affect the display of your site to users as well. Deciding where AJAX is best used On the whole, we're going to look at the most popular places where AJAX can really aid and enrich your site's user experience. We'll start with users adding comments to articles and pages and streamlining that process. We'll then take a look at a nice plugin that can enhance pagination for people reading long articles on your site. We'll then move on to the RSS Reader module, which can enhance the content in your modules (and even makes your users have fun arranging them). Finally, we'll realize that AJAX isn't just for impressing your site users. You, as an administrator, can (and do) take advantage of AJAX as well. Please note: These extensions were chosen by me based on the following criteria: 1. They provided some useful enhancement to a basic site.2. They, at the time of this writing, were free and received very good feedback on Joomla!.org's extensions site: http://extensions.Joomla.org. In the next few pages, I'll walk you through installing these extensions and discuss any interesting insights for doing so, and benefits of their enhancements (and some drawbacks). But you must use the extension links provided to make sure you download the latest stable versions of these extensions and follow the extension author's installation guides when installing these into your Joomla! site. If you run into any problems installing these extensions, please contact the extension's author for support. Always be sure to take the normal precaution of backing up your site before installation, at least for any non-stable extensions you may decide to try. Installing the Joomla! comment component Chances are, if you've invested in Joomla! 1.5 as your CMS, you need some powerful capabilities. Easy commenting with "captcha" images to reduce spam is always helpful: http://extensions.Joomla.org/extensions/contacts-&-feedback/comments/4389/details To install this extension (and the other few coming up), you have to basically go to Extensions | Install/Uninstall and upload the extension's ZIP file. You'll then proceed to the plugin, component, and/or modules panel and activate the extension so that it is ready to be implemented on your site. Upon installing this comment component, to my surprise, it told me that it was for an older version of Joomla! Everything on the download page seemed to indicate it worked with 1.5. The installation error did mention that I just needed to activate the System Legacy plugin and it would work. So I did, and the comment form appeared on all my article pages. This may seem like a step backward, but for extensions like this, which are very useful, if they work well and stay stable in Legacy Mode, a developer may have made the decision to leave well enough alone. The developer will most likely eventually upgrade the extension (especially if Legacy Mode goes away in future versions of Joomla!). Just be sure to sign up for updates or check back on any extensions you use if you do upgrade your site. You should do this regardless of whether your extensions run natively or in Legacy Mode. The advantage of AJAX in a comment form is that a user isn't distracted and comments post smoothly and right away (a bit of instant gratification for the user, even if you never "confirm" the post and it never gets actually published for other viewers). This extension outputs tables, but for the ease of handling robust comments and having a great admin area to manage them, I'll make do. The following screenshot shows the Joomla! comment component appearing in an article page: As you can see in my previous image, I have some strong styles that are trying to override the component's styles. A closer look at the output HTML will give me some class names and objects that I can target with CSS. The administration panel's Component | Joomla! Comment | Other Component settings page also allows quite a few customization options. The Layout tab also offers several included style sheets to select from as well as the option to copy the CSS sheet out to my template's directory (the component will do this automatically). This way, I can amend it with my own specific CSS, giving my comment form a better fit with my template's design. Installing the core design Ajax Pagebreak plugin If your site has long articles that get broken down regularly in to three or more pages, Pagebreak is a nice plugin that uses Ajax to smoothly load the next page. It's a useful feature that will also leave your site users with a little "oh wow" expression. http://www.greatJoomla.com/news/plugins/demo-core-design-ajaxpagebreak-plugin.html After successfully installing this plugin, I headed over to the Extensions | Plugin Manager and activated it. I then beefed out an article (with Lorem Ipsum) and added page breaks to it on the Home Page. It's hard to see in a screenshot, but it appears below the Prev and Next links without a full browser redraw. I've set my site up with SEO-friendly URLs, and this plugin does amend the URLs with a string; that is, http://yoururl.com/1.5dev/menu-item-4?start=1. I'm not sure how this will really affect the SEO "friendliness" value of my URL, but it does give me a specific URL to give to people if I want to send them to a targeted page, which is very good for accessibility. One thing to note, the first page of the article is the original URL; that is, http://yoururl.com/1.5dev/menu-item-4. The second page then appends ?start=1, the third page becomes ?start=2, and so on. Just be aware that when sending links out to people, it is always best to pull the URL directly from the site so that you know it's correct! Installing the AJAX RSS Reader Version 3 with Draggable Divs module RSS feeds are a great way to bring together a wide variety of content as well as bring all your or your organization's "social network happenings" to one place in your own site. I like to use RSS feeds to get people interested in knowing what an organization is doing (or tweeting), or reading, and so on. Having links and lists of what's currently going on can compel users to link to you, join your group, follow you, and become a friend, a fan, or whatever. This AJAX powered module has the extra feature of being draggable and somewhat editable. This is a nice way to draw a user in to the feeds and let them play with them and arrange the information to their taste. Sometimes, sorting and reorganizing makes you see connections and possibilities that you didn't see before. The next image may seem confusing, but it's a screenshot of the top div box being dragged and dropped: http://extensions.Joomla!.org/extensions/394/details AJAX: It's not just for your site's users I've already mentioned, when applied properly, how AJAX can aid in interface usability. Joomla! attempts to take advantage of this within its Administration panel by enhancing it with relevant information and compressing multiple page forms into one single screen area. Here's a quick look at how Joomla! already uses AJAX to enhance its Administration panel forms: The following image shows how the image uploader uses a "lightbox" div layer effect so that you can keep track of where you are in the content editor. In the next image, you can see how Joomla! helps keep the administration area cleared up by using smooth-sliding accordion panels. This helps you see everything on one page and have access to just what you need, when you need it.
Read more
  • 0
  • 0
  • 3125

article-image-maintaining-optimizing-and-upgrading-your-site-drupal-6-part-2
Packt
16 Oct 2009
9 min read
Save for later

Maintaining, Optimizing and Upgrading Your Site in Drupal 6: Part 2

Packt
16 Oct 2009
9 min read
Maintaining content As you continue to add content to your web site, you will need to ensure that your content is properly moderated, that old content is removed, and that changes to web site content are tracked. Creating content revisions Good Eatin' Goal: Create revisions of content to ensure that you have a complete record of changes to your web site's content. Additional modules needed: None. Basic steps We have simply updated our pages as necessary to add new functionality and content. However, if you have many editors, content that changes frequently, a need to view the history of a page, or need the ability to easily return to an old version of a page, you will want to store multiple revisions of your pages. To do this, carry out the following steps: Edit the content for which you want to create a new revision. Make the changes as needed and, before saving, expand the Revision information section. Select the Create new revision option and enter a message describing the changes that you have made to the node. When you save the content, you will see a new tab called Revisions. Clicking on this tab will show you a list of all of the revisions that have been created for the page. If you would like to return to an older version of the page, you can click the revert link. Or, if you want to remove an older revision, you can click the delete link to get rid of it permanently. You can control which users have access to the revision system by using the Permissions Manager. Drupal allows you to control which users can: view revisions, revert revisions, and delete revisions. If you want to force users to always create new revisions when editing content, edit the content type and then expand the Workflow settings. Change the default options to select the Create new revision option. When editors change content, the Create new revision option will be selected by default, and they will not be able to change the option unless they have the administer nodes permission. If you want to approve all revisions before publication, you can deselect the Published checkbox. Comparing content revisions Good Eatin' Goal: Compare the text of two different revisions of a page. Additional modules needed: Diff (http://drupal.org/project/diff). Basic steps Although the built-in functionality for creating revisions in Drupal works perfectly well, it can be difficult to review the changes that were made in each revision. The Diff module makes comparing revisions very easy. Begin by installing and activating the Diff module. To use the Diff module, simply view the revisions for any page. You will notice that the Revisions list has changed to allow you to select the revisions to be compared. Select the revisions to compare and then click on the Show diff button. Drupal will then display information about the text that has been changed, added, or deleted. Moderate content Good Eatin' Goal: Find questionable or offensive content, and remove it from your site, easily. Additional modules needed: Modr8 (http://drupal.org/project/modr8). Basic steps An unfortunate side effect of having a web site on the Internet is that, at some point, a malicious user will attempt to post inappropriate content on your site. If your site is extremely busy, you may find yourself with a large amount of content to review and approve. The Modr8 module can help you manage the workload and can send emails to users letting them know when their content has been approved or rejected. Begin by installing and activating the Modr8 module. The settings for the Modr8 module can be accessed by selecting Site configuration and then Modr8, from the Administer menu. The basic settings control how often logs are removed. Alternatively, you can choose to keep the logs forever. You can also change the number of items in the moderation queue to be displayed at a time, as well as the default action for the content that requires moderation. You can also configure the email settings for the moderation queue, including the text of the emails, and whether or not emails should be sent to the user who posted the content when their content is approved and/or when their content is rejected. You can also choose to send an email if the moderator does not take action for the item and wants to send a note to the author. If you would like new content to be added to the moderation queue automatically, you can edit the content type and select the In moderation queue setting in the workflow section. To view the moderation queue, select Content management and then Moderated content, from the Administer menu. The moderation queue appears as follows: From this page, you can approve, delete, or defer action on any content that needs moderation. After you make your changes, click Save to complete your selections. You can also display a log of all the moderation actions, by clicking on Reports and then Content moderation log. The moderation log appears as follows: Allowing users to report questionable or offensive content. Good Eatin' Goal: Get feedback from users to learn what they find offensive so the objectionable content can be removed. Additional modules needed: Abuse (http://drupal.org/project/abuse). Basic steps In the last task, we reviewed methods that allowed you to moderate every piece of content that is added to the site. However, this can be a time-intensive task if the proportion of content that you receive that is questionable is low. If this is the case, you can allow your users to help you to moderate the content by using the Abuse module, to let them report items that they find offensive. This strategy has a couple of advantages. Firstly, you are freed from the maintenance of pre-approving all content before it is published. Secondly, it ensures that the content meets community standards, rather than placing you or your editors in charge of defining community standards. The Abuse module also has a Watchlist component that allows you to flag content as suspicious or banned, and automatically move them into a queue for review by an administrator. Begin by downloading and installing the Abuse and Watchlist modules, both of which are included in the Abuse installation. We will begin by editing the Watchlist settings, which can be accessed by selecting Site configuration and then Watchlist settings, from the Administer menu. You can include any words that you want to, in the Watch list and Filtered/banned word list, depending on your target audience and your site's needs; just make sure that you enter one word per line. Items on the Watch list can be viewed while they are in the review queue, and items on the Filtered/banned word list will be hidden until they are reviewed. You can also control which items are automatically added to the Watch list or banned list, based on the Watchlist word settings configured above. You can also force moderation for specific types if they are more prone to abuse. We can now modify the Abuse Moderation settings by selecting Site configuration and then Abuse Moderation settings, from the Administer menu. The first setting controls what content types are subject to abuse reports. The next section of controls how abuse tickets are to be handled by your moderators. If you have multiple moderators for your site, you can select the Abuse Assigned Moderators option. If you use this, you will also need to store the maximum number of items that have been flagged for abuse that are added to the moderator's queue. If moderators live in different time zones, you can set an hour of the day at which all moderation queues are cleared, so that items do not remain in the moderation queue for an overly-long period of time. Finally, you can configure the settings related to all of the items that have been flagged as abusive by a user. The Abuse threshold controls how many complaints must be registered for an item before it is moved into the moderation queue. 3 is a good number to start with, but you may want to increase or decrease the threshold depending on the needs of your site. You can edit the reasons for flagging an item for abuse by selecting Site configuration, then Abuse Moderation settings, and finally Abuse Moderation reasons, from the Administer menu. All available reasons will be listed on the page using a format similar to the example above. You can add new reasons, remove reasons, or change the text for reasons from this page. Before the abuse module is activated, you need to assign permissions to users, so that they can flag content for review. Content that has the abuse module activated will have a new Flag as offensive link added to it, as shown in the following screenshot: When the user clicks on the Flag as offensive link, he or she will be presented with a form where he or she can specify their contact information, and a reason why he or she believes that the content is offensive. Administrators can review content that has been flagged as offensive by clicking on Content management and then Moderate. The administrators can click on the Get More Tickets link to have additional items assigned to them. Once a ticket has been assigned to them, the administrator can view information about the user who submitted the content as well as the user who flagged the content, and choose what action to take for the content. The administrator can either allow the content, or remove the content from the web site. The administrator can also optionally send a warning to the user without further action.
Read more
  • 0
  • 0
  • 1795
article-image-adding-interactive-course-material-moodle-19-part-1
Packt
16 Oct 2009
2 min read
Save for later

Adding Interactive Course Material in Moodle 1.9: Part 1

Packt
16 Oct 2009
2 min read
The following table gives you a brief description of each kind of activity. The sections that follow the table describe how and when to use these activities. Activity Description Assignment   An assignment is an activity completed offline, outside of Moodle. When the student completes the assignment, he or she either uploads a file for the instructor's review, or reports to the instructor in some other way. Regardless of whether the assignment requires uploading a file, the student receives a grade for the assignment. Choice   A choice is essentially a single, multiple-choice question that the instructor asks the class. The result can either be displayed to the class, or kept between the individual student and the instructor. Choices are a good way to get feedback from the students about the class. You can plant these choices in your course ahead of time, and keep them hidden until you need the students' feedback. You can also add them as needed. Journal   You can create an online journal, which will be unique for each student. A journal can be seen only by the student who writes it, and the instructor. Remember that a journal is attached to the course in which it appears. If you want to move a student's journal to another course, you'll need to make creative usage of the backup and restore functions.
Read more
  • 0
  • 0
  • 1303

article-image-maintaining-optimizing-and-upgrading-your-site-drupal-6-part-1
Packt
16 Oct 2009
7 min read
Save for later

Maintaining, Optimizing and Upgrading Your Site in Drupal 6: Part 1

Packt
16 Oct 2009
7 min read
We will consider the example of an imaginary web site created for a restaurant called Good Eatin' Bistro. Chef Wanyama is the owner of the Good Eatin' Bistro, a popular upscale restaurant. You can check this web site at http://goodeatin.drupalbyexample.com/. Web site backups A strong backup plan is critical for any successful web site. A good backup plan will protect against hardware failure, allow you to transfer your web site to another host, and allow you to recover from malicious hacking into your web site. When you create a backup plan, you should also test the restoration from this backup to make sure that the backup works correctly. In this section, we will explore ways of performing backups regardless of the host that you are using. Your hosting provider may also offer a solution that will back up files and databases either one time, or on a recurring basis. If your host does provide backup capabilities, you should review them to see if they suit your needs completely, or if you want to augment them or replace them with the techniques in this section. Manually backing up a site Good Eatin' Goal: Back up the web site without using a custom backup module. Additional modules needed: None. Basic steps If you do not want to use a dedicated module to perform your backups, you can manually download the files and the database information that make up the site. However, this can be more time-intensive and error-prone than using a custom backup module. A manual backup has two steps, in which you must first back up the files that make up the site and then back up the database information. To back up the files for the web site, use the following procedure: Begin by opening the utility that you use to transfer files to the web site. This could be an FTP client, or an online file manager. My favorite FTP client is FileZilla, which is a freely-available open source client. The FileZilla client can be downloaded from http://filezilla-project.org/. Select the backup location on your local computer to which you want to copy the files, and select the root directory of your web server as the remote directory. You may want to date the backup folder so that you can maintain a history of the site. Next, download the files to your local directory. If you want, you can compress the files into a ZIP file or a compressed archive. To reduce the amount of data that you need to download, you should be able to download just the sites directory, because that folder contains all of the custom files, pictures, themes, and modules that you have added to the site. To back up the database information, you can use your web site provider's database management utility. Many hosts provide phpMyAdmin for this purpose. If you are unsure whether or not your host gives you access to phpMyAdmin, you can contact their customer support group to check. Begin by opening phpMyAdmin and selecting the database that has your site information within it. The screen should be similar to the following: If you have multiple databases available on the host, you may need to select the database that you want to work with in the drop-down list at the upper left corner of the screen. Next, select the Export tab at the top of the screen. phpMyAdmin will prompt you to select the tables that you want to download and the format that you want to download in, as shown in the following screenshot: If you want to be able to rebuild the database at a later time, you should export all the tables in SQL format. Next, you will need to specify the name of the file to download to. You can use __DB__ as the database name. You may want to zip the file to reduce storage space. Then click Go to begin the download process. You will be prompted for the location to which you want to save the exported data. When you are ready to restore the web site from backup, you simply reverse the process. You should always import into a blank database, to avoid conflicts with existing data. You can either drop or delete all of the titles in the existing database, or you can create a new database to import the data into. After you have cleaned out your database, select the Import tab in phpMyAdmin. Now navigate to the file that you exported earlier, and click Go to begin the import. You may need to delete all of the tables in the database before you import the data, depending on the options you chose when you exported the data. To reload the files, simply open your FTP client, select the same directories that you used when creating the backup and then upload the files, rather than downloading them. Automatic site backups Good Eatin' Goal: Back up a web site so that it can be stored for easy recovery. Additional modules needed: Backup and Migrate (http://drupal.org/project/backup_migrate). Basic steps Although you can manually back up your files and database, this process can be time-consuming and error prone. Luckily, the Backup and Migrate module makes this process easier, and optimizes the backups to exclude unnecessary data. Begin by downloading and installing the Back up and Migrate module. You can now back up your data by selecting Content management and then Backup and migrate, from the Administer menu. The Backup and Migrate module allows you to fully customize the backup files that are created. You can control which tables are included in the backup, and whether or not the data in the table is backed up. By default, the Backup and Migrate module does not back up cache information, session information, or watchdog information, because data in these tables is temporary and can easily be re-created. There are a variety of other options that you can choose from, which control how the resulting file is named, how it is compressed, and where it is compressed to. Once you have set the options as desired, click Backup Database to begin the backup process. If you have selected the Download option, the file will be sent to your computer so that you can store it. If you select the Save to Files Directory option, the backup file will be saved onto the server so that you can download it later, either directly from the server or using the Saved Backups tab. If you would like the Backup and Migrate module to back up your database automatically on a regular basis, you can schedule the back up to occur at specified intervals by clicking on the Backup Schedule tab, as shown here: Please note that the backups created by the Backup and Migrate module do not include the files from the site, so you will still need to back up these files independently. You can minimize the backup file size by only backing up the files that the users can upload. These files are typically stored in the files directory. The process for backing up files is identical to the process used in the section on manual backups. Restoring a site from a backup Good Eatin' Goal: Restore information from a backup file created by the Backup and Migrate module. Additional modules needed: Backup and Migrate (http://drupal.org/project/backup_migrate). Basic steps Restoring a backup created by the Backup and Migrate module is a simple process. Navigate to the Backup and Migrate manager by selecting Content management and then Backup and Migrate, from the Administer menu. Next, click on the Restore/Import DB tab. Navigate to the location of your backup file. After you have selected the backup file, click on Restore Database to begin the restore process. Please read all displayed warnings carefully, and make sure that you test the import on a test installation for your site before running it on your production site. If you are sure that you want to proceed with the import, agree to the confirmation and click restore. You may also need to import any saved files, if the server file system is not fully up-to-date. We discussed this previously in the section on manual backups.
Read more
  • 0
  • 0
  • 1367

article-image-creating-new-types-plone-portlets
Packt
15 Oct 2009
4 min read
Save for later

Creating New Types of Plone Portlets

Packt
15 Oct 2009
4 min read
(For more resources on Plone, see here.) Plone makes it easy to create new types of portlets that include custom programming logic for your site. There are several ways to create custom portlets, but the simplest way to get started is to use the add-on product collective.portlet.tal which provides a new type of portlet, called a TAL Portlet. This portlet allows you to write simple bits of code using Zope's TAL templating language. Let's walk through a quick example of building a custom TAL portlet, which will show a randomly-selected news item from your site. Installing collective.portlet.tal Before you can add a TAL portlet, you must download the product from Plone.org/products and install the add-on product collective.portlet.tal on your site. The best way to do this is to modify your buildout.cfg file. Add collective.portlet.tal to the eggs and zcml sections of your buildout. Here's a code snippet with the changes made to it: [buildout] ... eggs = ... collective.portlet.tal [instance] recipe = plone.recipe.zope2instance ... zcml = collective.portlet.tal Once you've made these changes, re-run buildout by issuing the following command: $ ./bin/buildout Once you've added the product to your buildout, visit Site Setup and choose Add/Remove Products, to install collective.portlet.tal in your site. Finally, add a few news items to your site so that we have something for our new TAL portlet to find. Adding a simple TAL portlet With the collective.portlet.tal product in place, the following can happen: Navigate to your Plone site. Choose Manage Portlets in the right column. From the Add portlet... drop-down list, choose TAL Portlet. You'll see an empty text box in which you can enter a title. We will specify Featured News Item as our title. We'll soon see the code needed to feature a random one of our site's published news items. In addition to the Title text box, you'll also see an HTML text area titled TAL code. Conveniently, this comes pre-populated with some boilerplate HTML and TAL code. Skim this, so that you get a feel for how this looks and what the common HTML structure is like, for a portlet in Plone. As an immediate experiment, we will find the following snippet of code: <dd class="portletItem odd"> Body text</dd> We will modify this, slightly, to: <dd class="portletItem odd"> Is this thing on?</dd> Click on Save and navigate through the site, and you should see your first TAL portlet in action. Of course, there's nothing in this example that couldn't be accomplished with a static text portlet. So let's navigate back to the Featured News Item portlet and make it a bit more interesting and dynamic. Update the code in your TAL Portlet to include the following: <dl class="portlet portlet${portlet_type_name}" tal_define="newsitems python:context.portal_catalog (portal_type='News Item', review_state='published');" tal_condition="newsitems"> <dt class="portletHeader"> <span class="portletTopLeft"></span> <span> Featured News Item </span> <span class="portletTopRight"></span> </dt> <dd class="portletItem odd" tal_define="random_newsitem python:random.choice(newsitems)"> <a tal_content="random_newsitem/Title" href="[replaced by random news item link]" title="[replaced by random news item title]" tal_attributes="href random_newsitem/getURL; title random_newsitem/Title">[replaced by random news item title]</a> </dd> <dd class="portletFooter"> <span class="portletBotomLeft"></span> <span> <a href="http://example.com/news">More news...</a> </span> <span class="portletBottomRight"></span> </dd> </dl> Now, let's go into more detail on a few of these sections, so that you understand what's happening. If at any point you need more context, try reading the excellent ZPT reference manual at http://plone.org/documentation/tutorial/zpt.
Read more
  • 0
  • 0
  • 2158
article-image-working-javascript-drupal-6-part-1
Packt
15 Oct 2009
11 min read
Save for later

Working with JavaScript in Drupal 6: Part 1

Packt
15 Oct 2009
11 min read
How Drupal handles JavaScript How is JavaScript typically used? Mostly, it is used to provide additional functionality to a web page, which is usually delivered to a web browser as an HTML document. The browser receives the HTML from the server and then begins the process of displaying the page. During this parsing and rendering process, the browser may request additional resources from the server such as images, CSS, or Flash. It then incorporates these elements into the document displayed to the user. In this process, there are two ways that JavaScript code can be sent from the server to the browser. First, the code can be placed directly inside the HTML. This is done by inserting code inside the <script> and </script> tags: <script type="text/javascript">alert('hello world');</script> This is called including the script inline. Second, the code can be loaded separately from the rest of the HTML. Again, this is usually done using the <script> and </script> tags. However, instead of putting the code between the tags, we use the src attribute to instruct the browser to retrieve an additional document from the server. <script type="text/javascript" src="some/script.js"></script> In this example, src="some/script.js" points the browser to an additional script file stored on the same server as the HTML document in which this script tag is embedded. So, if the HTML is located at http://example.com/index.html, the browser will request the script file using the URL http://example.com/some/script.js. The </script> tag is required When XML was first standardized, it introduced a shorthand notation for writing tags that have no content. Instead of writing <p></p>, one could simply write <p/>. While this notation is supported by all modern mainstream browsers, it cannot be used for <script></script> tags. Some browsers do not recognize <script/> and expect that any <script> tag will be accompanied by a closing </script> tag even if there is no content between the tags. If we were developing static HTML files, we would simply write HTML pages that include <script></script> tags anytime we needed to add some JavaScript to the page. But we're using Drupal, not static HTML, and the process for adding JavaScript in this environment is done differently. Where Drupal JavaScript comes from? As with most web content management systems, Drupal generates HTML dynamically. This is done through interactions between the Drupal core, modules, and the theme system. A single request might involve several different modules. Each module is responsible for providing information for a specific portion of the resulting page. The theme system is used to transform that information from PHP data structures into HTML fragments, and then compose a full HTML document. But this raises some interesting questions: What part of Drupal should be responsible for deciding what JavaScript is needed for a page? And where will this JavaScript come from? In some cases, it makes sense for the Drupal core to handle JavaScript. It could automatically include JavaScript in cases where scripts are clearly needed. JavaScript can also be used to modify the look and feel of a site. In that case, the script is really functioning as a component of a theme. It would be best to include the script as a part of a theme. JavaScript can also provide functional improvements, especially when used with AJAX and related technologies. These features can be used to make more powerful modules. In that case, it makes sense to include the script as a part of a module. So which one is the best: modules, themes, or core? Rather than deciding on your behalf, Drupal developers have made it possible to incorporate JavaScript into all three: The Drupal core handles including the core JavaScript support as needed. The Drupal and jQuery libraries are included automatically when necessary. When theme developers needs to add some JavaScript, they can do so within the theme. There is no need to tamper with the core, or to accompany a theme with a module. Finally, module developers can add JavaScript directly to a module. In this way, modules can provide advanced JavaScript functionality without requiring modification of the theme. In this article we will add scripts to themes and modules. As we get started, we will begin with a theme. Module or theme? How do you decide whether your script ought to go in a theme or in a module? Here's a basic guideline. If the script provides functionality specific to the layout details of a theme, it should be included in a theme. If the script provides general behavior that should work regardless of the theme, then it should be included in a module. Sometimes it is hard to determine when a script belongs to a theme and when it should to be placed in a module. In fact, the script we create here will be one such a case. We are going to create a script that provides a printer-friendly version of a page's main content. Once we have the script, we will attach it to a theme. Of course, if we want to provide this functionality across themes, we might instead create a module to house the script. We will start out simply with a JavaScript-enabled theme. Project overview: printer-friendly page content The JavaScript that we will write creates a pop-up printer-friendly window, and automatically launches the print dialog. This is usually launched from File | Print in your browser's menu. Once we write the script, we will incorporate it into a theme, and add a special printing feature to the page(s) displayed with that theme. As we walk through this process, we will also create our first theme. (Technically, it will be a subtheme derived from the Bluemarine theme.) By the end of this project, you should know how to create Drupal-friendly JavaScript files. You will also know how to create themes and add scripts to them. The first step in the process is to write the JavaScript. The printer script Our script will fetch the main content of a page and then open a new window, populating that window's document with the main content of the page. From there, it will open the browser's print dialog, prompting the user to print the document. Since this is our first script, we will keep it simple. The code will be very basic, employing the sort of classical procedural JavaScript that web developers have been using since the mid-1990's. But don't expect this to be the norm. To minimize clutter and maximize the reusability of our code, we will store this new script in its own script file. The file will be named printer_tool.js: // $Id$/*** Add printer-friendly tool to page.*/var PrinterTool = {};PrinterTool.windowSettings = 'toolbar=no,location=no,' +'status=no,menu=no,scrollbars=yes,width=650,height=400';/*** Open a printer-friendly page and prompt for printing.* @param tagID* The ID of the tag that contains the material that should* be printed.*/PrinterTool.print = function (tagID) {var target = document.getElementById(tagID);var title = document.title;if(!target || target.childNodes.length === 0) {alert("Nothing to Print");return;}var content = target.innerHTML;var text = '<html><head><title>' +title +'</title><body>' +content +'</body></html>';printerWindow = window.open('', '', PrinterTool.windowSettings);printerWindow.document.open();printerWindow.document.write(text);printerWindow.document.close();printerWindow.print();}; First, let's talk about some of the structural aspects of the code. Drupal coding standards In general, well-formatted code is considered a mark of professionalism. In an open source project such as Drupal, where many people are likely to view and contribute to the code, enforced coding standards can make reading and understanding what the code does easier. When contributing code to the Drupal project, developers adhere to a Drupal coding standard (http://drupal.org/coding-standards). Add-on modules and themes are expected to abide by these rules. It is advised that you follow the Drupal standards even in code that you do no anticipate submitting to the Drupal project. Along with keeping your code stylistically similar to Drupal's, it will also help you develop good coding habits for those occasions when you do contribute something to the community. For the most part, the official Drupal coding standards are focused on the PHP code. But many of these rules are readily applicable to JavaScript as well. Here are a few important standards: Every file should have a comment near the top that has the contents $Id$. This is a placeholder for the version control system to insert version information. Drupal uses CVS (Concurrent Versioning System) for source code versioning. Each time a file is checked into CVS, it will replace $Id$ with information about the current version of the software. To learn more about CVS, visit http://www.nongnu.org/cvs/. Indenting should be done with two spaces (and no tabs). This keeps the code compact, but still clear. Comments should be used wherever necessary. Doxygen-style documentation blocks (/** ... */) should be used to comment files and functions. Any complex or potentially confusing code should be commented with // or /* ... */. Comments should be written in sentences with punctuation. Control structure keywords (if, else, for, switch, and so on) should appear at the beginning of a line, and be followed by a single space (if (), not if()). Here's an example: if (a) {// Put code here.}else if (b) {// Put code here.}else {// Put code here.} Operators (+, =, *, &&, ||, and so on) should have a single space on each side, for example: 1 + 2. The exception to this rule is the member operator (.), which is used to access a property of an object. There should be no spaces surrounding these. Example: window.document (never window . document). Stylistic differences between PHP and JavaScript Not all PHP coding standards apply to JavaScript. PHP variables and function names are declared in all lower case with underscores (_) to separate words. JavaScript typically follows different conventions. JavaScript variables and functions are named using camel case (sometimes called StudlyCaps). For a variable or function, the first word is all lower case. Any subsequent words in the variable or function name are capitalized. Underscores are not used to separate words. Here are some examples: var myNewVariable = "Hello World";function helloWorld() { alert(myNewVariable);} While this convention is employed throughout the Drupal JavaScript code, there is currently no hard-and-fast set of JavaScript-specific coding conventions. The working draft, which covers most of the important recommendations, can be found at http://drupal.org/node/260140. Here is a summary of the more important (and widely followed) conventions: Variables should always be declared with the var keyword. This can go a long way towards making the scope of variables explicit. JavaScript has a particularly broad notion of scope. Functions inherit the scope of their parent context, which means a parent's variables are available to the children. Using var makes it easier to visually identify the scoping of a variable. It also helps to avoid ambiguous cases which may lead to hard-to-diagnose bugs or issues. Statements should always end with a semicolon (;). This includes statements that assign functions, for example, myFunction = function() {};. Our print function, defined earlier, exhibits this behavior. Why do we require trailing semicolons? In JavaScript, placing semicolons at the end of statements is considered optional. Without semicolons, the script interpreter is responsible for determining where the statement ends. It usually uses line endings to help determine this. However, explicitly using semicolons can be helpful. For example, JavaScript can be compressed by removing whitespace and line endings. For this to work, every line must end with a semicolon. When an anonymous function is declared, there should be a space between  the function and the parentheses, for example, function () {}, not function() {}. This preserves the whitespace that would be there in in a non-anonymous function declaration (function myFunction() {}). There are other conventions, many of which you will see here. But the ones mentioned here cover the most frequently needed. With coding standards behind us, let's take a look at the beginning of the printer_tool.js file.
Read more
  • 0
  • 0
  • 6280

article-image-layout-dojo-part-1
Packt
15 Oct 2009
17 min read
Save for later

Layout in Dojo: Part 1

Packt
15 Oct 2009
17 min read
Basic Dojo layout facts The Layout widgets in Dojo are varied in nature, but their most common use is as 'windows' or areas which organize and present other widgets or information. Several use the same kind of child elements the ContentPane. The ContentPane is a widget which can contain other widgets and plain HTML, reload content using Ajax and so on. The ContentPane can also be used stand-alone in a page, but is more usable inside a layout container of some sort. And what is a layout container? Well, it's a widget which contains ContentPanes, of course. A layout container can often contain other widgets as well, but most containers work very well with a different configuration of ContentPanes, which properly insulates the further contents. Take the TabContainer, for example. It is used to organize two or more ContentPanes, where each gets its own tab. When a user clicks on one of the tabs, the ContentPane inside it is shown and all others are hidden. Using BorderManager can bring the necessary CSS styling down to a minimum, while giving a simple interface for managing dynamic changes of child widgets and elements. ContentPane A ContentPane can look like anything of course, so it doesn't really help putting a screen-dump of one on the page. However, the interface is very good to know. The following arguments are detected by ContentPane and can be used when creating one either programmatically or by markup: // href: String //The href of the content that displays now. //Set this at construction if you want to load data externally //when the pane is shown.(Set preload=true to load it immediately.) //Changing href after creation doesn't have any effect; //see setHref(); href: "", //extractContent: Boolean //Extract visible content from inside of <body> .... </body> extractContent: false, //parseOnLoad: Boolean //parse content and create the widgets, if any parseOnLoad:true, //preventCache: Boolean //Cache content retreived externally preventCache:false, //preload: Boolean //Force load of data even if pane is hidden. preload: false, //refreshOnShow: Boolean //Refresh (re-download) content when pane goes from hidden to shown refreshOnShow: false, //loadingMessage: String //Message that shows while downloading loadingMessage: "<span class='dijitContentPaneLoading'>$ {loadingState}</span>", //errorMessage: String //Message that shows if an error occurs errorMessage: "<span class='dijitContentPaneError'>${errorState}</span>", You don't need any of those, of course. A simple way to create a ContentPane would be: var pane = new dojo.layout.ContentPane({}); And a more common example would be the following: var panediv = dojo.byId('panediv');var pane = new dojo.layout.ContentPane({ href: "/foo/content.html", preload: true}, panediv); where we would have an element already in the page with the id 'panediv'. As you see, there are also a couple of properties that manage caching and parsing of contents. At times, you want your ContentPane to parse and render any content inside it (if it contains other widgets), whereas other times you might not (if it contains a source code listing, for instance). You will see additional properties being passed in the creation of a ContentPane which are not part of the ContentPane itself, but are properties that give information specific to the surrounding Container. For example, the TabContainer wants to know which tab this is, and so on. Container functions All container widgets arrange other widgets, and so have a lot of common functionality defined in the dijit._Container class. The following functions are provided for all Container widgets: addChild: Adds a child widget to the container. removeChild: Removes a child widget from the container. destroyDescendants: Iterates over all children, calling destroy on each. getChildren: Returns an array containing references to all children. hasChildren: Returns a boolean. LayoutContainer The LayoutContainer is a widget which lays out children widgets according to one of five alignments: right, left, top, bottom, or client. Client means "whatever is left", basically. The widgets being organized need not be ContentPanes, but this is normally the case. Each widget then gets to set a layoutAlign property, like this: layoutAlign = "left". The normal way to use LayoutContainer is to define it using markup in the page, and then define the widgets to be laid out inside it. LayoutContainer has been superceeded by BorderContainer, and will be removed in Dojo version 2.0. SplitContainer The SplitContainer creates a horizontal or vertical split bar between two or more child widgets. A markup declaration of a SplitContainer can look like this: <div dojoType="dijit.layout.SplitContainer" orientation="vertical" sizerWidth="7" activeSizing="false" style="border: 1px solid #bfbfbf; float: left; margin-right: 30px; width: 400px; height: 300px;"> The SplitContainer must have a defined height and width. The orientation property is self-explanatory, as is sizerWidth. The property activeSizing means, if set to true, that the child widgets will be continually resized when the user changes the position of the sizer. This can be bad if the child widgets are complex or access remote information to render themselves, in which case the setting can be set to false, as in the above example. Then the resize event will only be sent to the child widgets when the user stops. Each child widget needs to define the sizeMin and sizeShare attributes. The sizeMin attribute defines the minimum size for the widget in pixels, but the sizeShare attribute is a relative value for the share of space this widget takes in relation to the other widget's sizeShare values. If we have three widgets inside the SplitPane with sizeShare values of 10, 40 and 50, they will have the same ratios in size as if the values had been 1:4:5. StackContainer The StackContainer hides all children widgets but only one at any given time, and is one of the base classes for both the Accordion and TabContainers. StackContainer exists as a separate widget to allow you to define how and when the child widgets are shown. Maybe you would like to define a special kind of control for changing between child widget views, or maybe you want other events in your application to make the Container show specific widgets. Either way, the StackContainer is one of the most versatile Containers, along with the BorderContainer. The following functions are provided for interacting with the StackContainer: back - Selects and shows the previous child widget. forward - Selects and shows the next child widget. getNextSibling - Returns a reference to the next child widget. getPreviousSibling - Returns a reference to the previous child widget. selectChild - Takes a reference to the child widget to select and show.   Here is the slightly abbreviated markup for the test shown above (test_StackContainer.html): <div id="myStackContainer" dojoType="dijit.layout.StackContainer" style="width: 90%; border: 1px solid #9b9b9b; height: 20em; margin: 0.5em 0 0.5em 0; padding: 0.5em;"> <p id="page1" dojoType="dijit.layout.ContentPane" title= "page 1">IT WAS the best of times, ....</p> <p id="page2" dojoType="dijit.layout.ContentPane" title= "page 2">There were a king with a large jaw ...</p> <p id="page3" dojoType="dijit.layout.ContentPane" title= "page 3">It was the year of Our Lord one thousand seven hundred and seventy- five. .../p></div> The StackContainer also publishes topics on certain events which can be caught using the messaging system. The topics are: [widgetId]-addChild [widgetId]-removeChild [widgetId]-selectChild [widgetId]-containerKeyPress Where [widgetId] is the id of this widget. So if you had a StackContainer defined in the following manner: <div id="myStackContainer" dojoType="dijit.layout.StackContainer">...</div> You can use the following code to listen to events from your StackContainer: dojo.subscribe("myStackContainer-addChild", this, function(arg){ var child = arg[0]; var index = arg[1];}); Compare with the following code from the StackContainer class itself: addChild: function(/*Widget*/ child, /*Integer?*/ insertIndex) { // summary: Adds a widget to the stack this.inherited(arguments); if(this._started) { // in case the tab titles have overflowed from one line // to two lines this.layout(); dojo.publish(this.id+"-addChild", [child, insertIndex]); // if this is the first child, then select it if(!this.selectedChildWidget) { this.selectChild(child); } } }, Also declared in the class file for the StackContainer is the dijit.layout.StackController. This is a sample implementation of a separate widget which presents user controls for stepping forward, backward, and so on in the widget stack. What differentiates this widget from the Tabs in the TabContainer, for example, is that the widget is completely separate and uses the message bus to listen to events from the StackContainer. You can use it as-is, or subclass it as a base for you own controllers. But naturally, you can build whatever you want and connect the events to the forward() and back() function on the StackContainer. It's interesting to note that at the end of the files that define StackContainer, the _Widget base class for all widgets is extended in the following way: //These arguments can be specified for the children of a//StackContainer.//Since any widget can be specified as a StackContainer child,//mix them into the base widget class. (This is a hack, but it's//effective.)dojo.extend(dijit._Widget, {//title: String//Title of this widget.Used by TabContainer to the name the tab, etc.title: "",//selected: Boolean//Is this child currently selected?selected: false,//closable: Boolean//True if user can close (destroy) this child, such as//(for example) clicking the X on the tab.closable: false, //true if user can close this tab paneonClose: function(){//summary: Callback if someone tries to close the child, child//will be closed if func returns true return true; }}); This means that all child widgets inside a StackContainer (or Tab or AccordionContainer) can define the above properties, which will be respected and used accordingly. However, since the properties are applied to the _Widget superclass they are of course now generic to all widgets, even those not used inside any containers at all. The most commonly used property is the closable property, which adds a close icon to the widget and title, which defines a title for the tab. A lot of Dijits respond to keypress events, according to WAI rules. Let's look at the code that is responsible for managing key events in StackContainer and all its descendants: onkeypress: function(/*Event*/ e){ //summary: //Handle keystrokes on the page list, for advancing to next/previous button //and closing the current page if the page is closable. if(this.disabled || e.altKey ){ return; } var forward = null; if(e.ctrlKey || !e._djpage){ var k = dojo.keys; switch(e.charOrCode){ case k.LEFT_ARROW: case k.UP_ARROW: if(!e._djpage){ forward = false; } break; case k.PAGE_UP: if(e.ctrlKey){ forward = false; } break; case k.RIGHT_ARROW: case k.DOWN_ARROW: if(!e._djpage){ forward = true; } break; case k.PAGE_DOWN: if(e.ctrlKey){ forward = true; } break; case k.DELETE: if(this._currentChild.closable) { this.onCloseButtonClick(this._currentChild); } dojo.stopEvent(e); break; default: if(e.ctrlKey){ if(e.charOrCode == k.TAB) { this.adjacent(!.shiftKey).onClick(); dojo.stopEvent(e); } else if(e.charOrCode == "w") { if(this._currentChild.closable) { this.onCloseButtonClick(this._currentChild); } dojo.stopEvent(e); // avoid browser tab closing. } } } // handle page navigation if(forward !== null) { this.adjacent(forward).onClick(); dojo.stopEvent(e); } }}, The code is a very good example on how to handle key press events in Dojo in its own right, but for our purposes we can summarize in the following way: If UP, LEFT, or SHIFT+TAB is pressed, forward is set to false, and the last block of code will use that as an argument to the adjacent function which returns the prior child widget if false and the next child widget if true. In this case, the former. If DOWN, RIGHT, or TAB is pressed, forward will be set to true, which will declare the next child widget to be activated and shown. If DELETE or w is pressed and the current child widget is closable, it will be destroyed. TabContainer The TabContainer, which derives from StackContainer, organizes all its children into tabs, which are shown one at a time. As you can see in the picture below, the TabContainer can also manage hierarchical versions of itself. The TabContainer takes an argument property called tabPosition, which controls where the tab icons are displayed for each tab. Possible values are "top", "bottom", "left-h", "right-h", with "top" as default. There are no special functions provided for TabContainer, which adds very little logic to that provided from the StackContainer superclass. AccordionContainer The AccorionContainer shows a horizontal bar for each added child widget, which represents its collapsed state. The bar acts as a tab and also holds the title defined for the child widget. When the bar is clicked, an animation hides the current widget, and also animates in the widget whose bar was clicked. The abbreviated code for the test case above (test_accordionContainer.html) is here: <div dojoType="dijit.layout.AccordionContainer" style="width: 400px; height: 300px; overflow: hidden"> <div dojoType="dijit.layout.AccordionPane" title="a"> Hello World </div> <div dojoType="dijit.layout.AccordionPane" title="b"> <p> Nunc consequat nisi ... </p> <p> Sed arcu magna... </p> </div> <div dojoType="dijit.layout.AccordionPane" title="c"> <p>The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog.</p> </div></div>  A funny thing about the AccordionContainer is that it requires not any old widget as a child node, but its own AccordionPane, as you see in the code above. However, the AccordionPane has ContentPane as superclass, and defines itself slightly differently due to the special looks of an accordion. Also, the AccordionPane does not currently support nested Layout widgets, even though single-level widgets are supported. BorderContainer The BorderContainer has replaced the functionality of both LayoutContainer and SplitContainer. Note that the outermost BorderContainer widget does not carry any layout information. This is instead delegated to each individual widget. As each child gets added to the BorderContainer, the layout is recalculated. Using the BorderContainer is a very good alternative to using CSS-based "tableless tables". For using the BorderContainer, you don't need any other rules, and the Container recalculates positioning automatically, without the need for additional CSS rules (except for the height/width case below) each time you add an element or widget to the area. Since BorderContainer widget replaces both SplitContainer and LayoutContainer, it both lets its child widgets declare where they are in relation to each other. Optionally, add resizing splitter between children. Also, instead of optionally declaring one child as "client", one child must now always be declared as "center". For some reason, the child widget now use region, instead of layoutAlign, so a child widget which would have been defined like this in LayoutContainer: <div dojoType="dijit.layout.ContentPane" layoutAlign="top">...</div> is now defined like this instead: <div dojoType="dijit.layout.ContentPane" region="top">...</div> All "side" widgets must define a width, in style, by CSS class or otherwise, and the same applies for top/bottom widgets, but with height. Center widgets must not declare either height or width, since they use whatever is left over from the other widgets. You can also use leading and trailing instead of right and left. The only difference is that when you change locale to a region that has text going from right to left (like Arabic and many others), this will arrange the widgets appropriate to the locale. The BorderContainer also takes an optional design property, which defines if the BorderContainer is a headline or sidebar. The headline is the default and looks like the picture below. headline means that the top and bottom widgets extend the full length of the container, whereas sidebar means the the right and left (or leading and trailing) widgets extend top to bottom. The sizeShare attribute for the ContentPanes used in the SplitContainer is deprecated in BorderContainer. All ContentPanes sizes are defined using regular techniques (direct stylin, classes, and so on). From the BorderContainer test located in dijit/tests/layout/test_BorderContainer_nested.html, we find the following layout: The (abbreviated) source code for the example is here: <div dojoType="dijit.layout.BorderContainer" style="border: 2px solid black; width: 90%; height: 500px; padding: 10px;"> <div dojoType="dijit.layout.ContentPane" region="left" style="background-color: #acb386; width: 100px;"> left </div> <div dojoType="dijit.layout.ContentPane" region="right" style="background-color: #acb386; width: 100px;"> right </div> <div dojoType="dijit.layout.ContentPane" region="top" style="background-color: #b39b86; height: 100px;"> top bar </div> <div dojoType="dijit.layout.ContentPane" region="bottom" style="background-color: #b39b86; height: 100px;"> bottom bar </div> <div dojoType="dijit.layout.ContentPane" region="center" style="background-color: #f5ffbf; padding: 0px;"> <div dojoType="dijit.layout.BorderContainer" design="sidebar" style="border: 2px solid black; height: 300px;"> <div dojoType="dijit.layout.ContentPane" region="left" style="background-color: #acb386; width: 100px;"> left </div> <div dojoType="dijit.layout.ContentPane" region="right" style="background-color: #acb386; width: 100px;"> right </div> <div dojoType="dijit.layout.ContentPane" region="top" style="background-color: #b39b86; height: 100px;"> top bar </div> <div dojoType="dijit.layout.ContentPane" region="bottom" style="background-color: #b39b86; height: 100px;"> bottom bar </div> <div dojoType="dijit.layout.ContentPane" region="center" style="background-color: #f5ffbf; padding: 10px;"> main panel with <a href="http://www.dojotoolkit.org/"> a link</a>.<br /> (to check we're copying children around properly). <br /> <select dojoType="dijit.form.FilteringSelect"> <option value="1">foo</option> <option value="2">bar</option> <option value="3">baz</option> </select> Here's some text that comes AFTER the combo box. </div> </div> </div></div> You see here the recurring theme of using ContentPanes inside Containers. Also, the innermost "center" ContentPane wraps a new BorderContainer which has its own internal top/left layout widgets. Depending on what kind of application you are building, the BorderContainer might be a good starting point. Since you already know that you can change and reload the contents of individual ContentPanes, you are left with a layout in which each element can function as a lightweight Iframe with none of the negative side effects. DragPane The DragPane is a very simple idea. You have a very large area of elements to display, and want to let the user 'drag' the underlying pane around using the mouse. The DragPane can be used in instances where you have a lot of pictures to present. It can also be used to present text or other widgets that are too numerous to fit in your current designated area of screen real estate. The only property of DragPane is invert, which if set to true, inverts the axis of the drag of the mouse. Example: <div class="hugetext" id="container" invert="false" dojoType="dojox.layout.DragPane"> <p style="color:#666; padding:8px; margin:0;"> Lorem ipsum dolor sit amet, consectetuer adipiscing elit. In porta. Etiam mattis libero nec ante. Nam porta lacus eu ligula. Cras mauris. Suspendisse vel augue. Vivamus aliquam orci ut eros. Nunc eleifend sagittis turpis. purus purus in nibh. Phasellus in nunc. </p></div>
Read more
  • 0
  • 0
  • 3136
Modal Close icon
Modal Close icon