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
Events
Videos
Audiobooks
Packt Hub
Free Learning
Arrow right icon
timer SALE ENDS IN
0 Days
:
00 Hours
:
00 Minutes
:
00 Seconds

How-To Tutorials

7019 Articles
article-image-managing-pages-liferay-portal-52-systems-development
Packt
21 Oct 2009
14 min read
Save for later

Managing Pages in Liferay Portal 5.2 Systems Development

Packt
21 Oct 2009
14 min read
Each site is represented as a community and each community is made up of a lot of pages, for example, public pages and private pages. In order to build web sites, we need to manage communities and, further, manage pages for each community. The Communities portlet provides the ability to create and manage communities and their users, as well as of the Manage Pages portlet. Extending Communities portlet The Communities portlet provides the ability to create and manage communities and their users. A community is a special group holding a number of users who share common interests. By default, a community is represented by the Group_ table with fields such as groupId, companyId, creatorUserId, name, description, type, typeSettings, friendlyURL, active, and so on. Now let's take an in-depth look at the customization of the community. As shown in the following screenshot, we may want to add one searchable field for each community, which is Keywords. For example, suppose we are creating a new community with the name Book Street, and the description a community for website www.bookpubstreet.com. Now we have a chance to add the new Keywords field with the value, for example, Book; Street; Palm Tree; Publication. Similarly, when editing the properties of a community—for example Name, Description, Type, and Active—we again have a chance to edit Keywords. In addition, we expect to have more fields in the customized communities: Created (when the community was created), ModifierUserId (who modified the community), and Modified (when the community was modified). As shown in the preceding screenshot, when listing communities, not only should the default fields (for example, Name, Type, Members, Online Now, Active, Pending Requests) and Actions icons (for example, Edit, Permissions, Manage Pages, Assign User Roles, Assign Members, Leave, and Delete) be displayed, but also the customized columns (for example, the username and Keywords) should be displayed. How do we implement these features? In this article, we're going to show how to customize the Communities portlet using the above requirements as examples. Obviously, it is open for you to customize this portlet in a number of ways according to your own requirements. In general, the processes for customization of this portlet should be the same. Building Ext Communities portlet The Communities portlet can be used to create and manage new portal communities and their users. As you can see, a community can be regarded as a separate portal instance; each community gets its own set of pages, content management system, shared calendar, and default permissions. Moreover, a user belonging to multiple communities can navigate among them within the same portal session. Generally speaking, we do not want to update the Communities portlet, but keep it as it is. Our goal is to customize and extend it. In general, this can be done by using the following two steps: Build a customized Ext Communities portlet, which has exactly the same functions, look, and feel as that of the original Communities portlet. Extend this customized portlet and let it have an additional model and service, and moreover, its own look and feel. In this part, let's build the Ext Communities portlet, having exactly same functions, look, and feel as that of the Communities portlet. Constructing the portlet Now let's define a Struts portlet with the name "Ext Communities". We first need to configure it in both portlet-ext.xml and liferay-portlet-ext.xml, and then set the title in Language-ext.properties, and then add the Ext Communities portlet to the Book category in liferay-display.xml. Locate the portlet-ext.xml file in the /ext/ext-web/docroot/WEB-INF folder and open it. Add the following lines between </portlet> and </portlet-app> and save the file: <portlet> <portlet-name>extCommunities</portlet-name> <display-name>Ext Communities</display-name> <portlet-class>com.liferay.portlet.StrutsPortlet</portlet-class> <init-param><name>view-action</name> <value>/ext/communities/view</value></init-param> <expiration-cache>0</expiration-cache> <supports><mime-type>text/html</mime-type></supports> <resource-bundle> com.liferay.portlet.StrutsResourceBundle </resource-bundle> <security-role-ref> <role-name>power-user</role-name> </security-role-ref> <security-role-ref> <role-name>user</role-name> </security-role-ref> </portlet> As shown in the code above, the portlet-name element contains the canonical name of the portlet (for example, extCommunities). The display-name element contains a short name that is intended to be displayed in the portal (for example, Ext Communities). The portlet-class element contains the fully qualified class name of the portlet (for example, com.liferay.portlet.StrutsPortlet). The init-param element contains a name-value pair, for example view-action-ext/communities/view, as an initialization parameter of the portlet. Further, the expiration-cache defines expiration-based caching for this portlet. The supports element contains the supported MIME-type. The resource-bundle element contains a resource bundle class, for example com.liferay.portlet. StrutsResourceBundle. Finally, the security-role-ref element contains the declaration of a security role reference in the code of the web application. Secondly, let's register the extCommunities portlet in liferay-portlet-ext.xml as follows: Locate the liferay-portlet-ext.xml file in the /ext/ext-web/docroot/WEB-INF folder and open it. Add the following lines immediately after <!-- Custom Portlets --> and save it: <portlet> <portlet-name>extCommunities</portlet-name> <struts-path>ext/communities</struts-path> <use-default-template>false</use-default-template> <restore-current-view>false</restore-current-view> </portlet> As shown in the code above, the Ext Communities portlet is registered in the portal. The portal will check struts-path to see whether a user has the required permissions to access the portlet or not. As you can see, struts-path has the value ext/communities. It means that all requests to the ext/communities/* path are considered a part of this portlet scope. Only those users whose request paths match ext/communities/* will be granted access. Moreover, the use-default-template element has the value false, so the portlet will not use any user's default template. The restore-current-view element has the value false so the portlet will reset the current view when toggling between maximized and normal states. Thirdly, add a title (for example, Ext Communities), for the Ext Communities portlet at Language-ext.properties as follows: Locate the Language-ext.properties file in the /ext/ext-impl/src/content folder and open it. Add the following line after javax.portlet.title.book_reports=Reports for Books and save it: javax.portlet.title.extCommunities=Ext Communities The code above provides mapping for the title of the portlet. If the mapping is not provided, the portal will show the default title javax.portlet.title.extCommunities. Finally, add the Ext Communities portlet to the Book category in liferay-display.xml as follows: Locate the liferay-display.xml file in the /ext/ext-web/docroot/WEBINF folder and open it. Add the following line immediately after the line <portlet id="book_reports" /> and save it: <portlet id="extCommunities" /> As shown in the code above, it adds the Ext Communities portlet to the category Book. From now on, you are able to select this portlet from the Book category directly when adding portlets to pages. Setting up actions Now, let's set up all actions required for the Ext Communities portlet. We need to prepare an action class, for example, ExtEditGroupAction. So how do we build this action? You can build the actions from scratch, but our purpose is to customize and extend the Communities portlet. In one word, we expect to reuse the out of the box portlet source code as much as possible and to write minimum code. As mentioned earlier, we have the portal project for the portal source code in the Eclipse IDE, which is referred to as the /portal prefix. We also have the ext project for customized code, which is referred to as the /ext prefix. The following is a process flow to build the ExtEditGroupAction action class of the Ext Communities portlet. Create a com.ext.portlet.communities.action package in the /ext/ext-impl/src folder. Create an ExtEditGroupAction class in this package and open it. Add the following lines and save it: public class ExtEditGroupAction extends EditGroupAction { public void processAction( ActionMapping mapping, ActionForm form, PortletConfig portletConfig, ActionRequest actionRequest, ActionResponse actionResponse) throws Exception { String cmd = ParamUtil.getString(actionRequest, Constants.CMD); try { if (cmd.equals(Constants.ADD) || cmd.equals(Constants.UPDATE)) { updateGroup(actionRequest); } else if (cmd.equals(Constants.DELETE)) { deleteGroup(actionRequest); } sendRedirect(actionRequest, actionResponse); } catch (Exception e) { if (e instanceof NoSuchGroupException || e instanceof PrincipalException) { SessionErrors.add(actionRequest, e.getClass().getName()); setForward(actionRequest, "portlet.ext.communities.error"); } else if (e instanceof DuplicateGroupException || e instanceof GroupFriendlyURLException || e instanceof GroupNameException || e instanceof RequiredGroupException) { SessionErrors.add(actionRequest, e.getClass().getName(), e); if (cmd.equals(Constants.DELETE)) { actionResponse.sendRedirect( ParamUtil.getString(actionRequest, "redirect")); } } else { throw e;} } } public ActionForward render(ActionMapping mapping, ActionForm form, PortletConfig portlonfig, RenderRequest renderRequest, RenderResponse renderResponse) throws Exception { try { ActionUtil.getGroup(renderRequest); } catch (Exception e) { if (e instanceof NoSuchGroupException || e instanceof PrincipalException) { SessionErrors.add(renderRequest, e.getClass().getName()); return mapping.findForward ("portlet.ext.communities.error"); } else {throw e;} } return mapping.findForward(getForward(renderRequest, "portlet.ext.communities.edit_community")); } } As shown in the code above, ExtEditGroupAction extends EditGroupAction from the com.liferay.portlet.communities.action package in the /portal/portal-impl/src folder. It overrides two methods (render and processAction) of EditGroupAction. Setting up page flow and page layout We have set up the action. We have also updated the forward path as the portlet.ext.communities.* value. In order to get the page flow working, we need to set up an action path and a page flow. First, let's set up the action path and page flow in struts-config.xml as follows: Locate the struts-config.xml file in the /ext/ext-web/docroot/WEB-INF folder and open it. Add the following lines after <struts-config> <action-mappings> and save it: <!-- Ext Communities --> <action path="/ext/communities/edit_community" type="com.ext.portlet.communities.action. ExtEditGroupAction"> <forward name="portlet.ext.communities.edit_community" path="portlet.ext.communities.edit_community" /> <forward name="portlet.ext.communities.error" path="portlet.ext.communities.error" /> </action> <action path="/ext/communities/view" forward="portlet.ext.communities.view" /> The code above defines a set of action paths associated with the action and forward paths, as well as those mentioned earlier. For example, the action path /ext/communities/edit_community is associated with the com.ext.portlet.communities.action.ExtEditGroupAction action and the forward path names portlet.ext.communities.edit_community and portlet.ext.communities.error. Then based on the page flow and JSP files, let's define the page layout in tiles-defs.xml: Locate the tiles-defs.xml file in the ext/ext-web/docroot/WEB-INF folder and open it. Add the following lines after <struts-config> <action-mappings> and save it: <!-- Ext Communities --> <action path="/ext/communities/edit_community" type="com.ext.portlet.communities.action. ExtEditGroupAction"> <forward name="portlet.ext.communities.edit_community" path="portlet.ext.communities.edit_community" /> <forward name="portlet.ext.communities.error" path="portlet.ext.communities.error" /> </action> <action path="/ext/communities/view" forward="portlet.ext.communities.view" /> The code above defines a set of action paths associated with the action and forward paths, as well as those mentioned earlier. For example, the action path /ext/communities/edit_community is associated with the com.ext.portlet.communities.action.ExtEditGroupAction action and the forward path names portlet.ext.communities.edit_community and portlet.ext.communities.error. Then based on the page flow and JSP files, let's define the page layout in tiles-defs.xml: Locate the tiles-defs.xml file in the ext/ext-web/docroot/WEB-INF folder and open it. Add the following lines after <tiles-definitions> and save it: <!-- Ext Communities --> <definition name="portlet.ext.communities" extends="portlet" /> <definition name="portlet.ext.communities.edit_community" extends="portlet.ext.communities"> <put name="portlet_content" value="/portlet/ext/communities/edit_community.jsp" /> </definition> <definition name="portlet.ext.communities.view" extends="portlet"> <put name="portlet_content" value="/portlet/ext/communities/view.jsp" /> </definition> <definition name="portlet.ext.communities.error" extends="portlet"> <put name="portlet_content" value="/portlet/communities/error.jsp" /> </definition> The code above defines the page layout for the Ext Communities portlet. For example, portlet.ext.communities.edit_community is associated with the JSP file /portlet/ext/communities/edit_community.jsp. In addition, it specifies that the community view page layout (for example, portlet.ext.communities.view) is associated with the JSP page file /portlet/ext/communities/view.jsp. Preparing JSP files We have now set up the actions. We have also set up page flow and page layout. Now let's set up the JSP files that are required for the Ext Communities portlet. We need to prepare JSP files such as view.jsp, edit_community.jsp, group_search.jsp, and so on. So how do we build this? You can build them from scratch. However, here we will copy and modify JSP files of the Communities portlet. In this section we expect to reuse the source code, including JSP files, as much as possible. First, let's create the view.jsp JSP file as follows: Create a communities folder within the /ext/ext-web/docroot/html/portlet/ext/ folder. Locate the view.jsp JSP file in the /portal/portal-web/docroot/html/portlet/communities folder, and copy it to the /ext/ext-web/docroot/html/portlet/ext/communities folder. Open view.jsp in the /ext/ext-web/docroot/html/portlet/ext/communities folder, update /communities/edit_community with /ext/communities/edit_community as shown in the following two lines, and save it: portletURL.setParameter("struts_action", "/ext/communities/view"); <liferay-ui:search-form page="/html/portlet/ext/communities/group_search.jsp" searchContainer="<%= searchContainer %>" showAddButton="<%= showTabs1 %>" /> Next, we need to create the JSP file edit_community.jsp as follows: Locate the JSP file edit_community.jsp in the /portal/portal-web/docroot/html/portlet/communities folder, and copy it to the /ext/extweb/docroot/html/portlet/ext/communities folder. Open edit_community.jsp in the /ext/ext-web/docroot/html/portlet/ext/communities folder, update /communities/edit_community with /ext/communities/edit_community as shown in following line, and save it: <form action="<portlet:actionURL windowState="<%= WindowState.MAXIMIZED. toString() %>"> <portlet:param name="struts_action" value="/ext/communities/edit_community" /> </portlet:actionURL>" method="post" name="<portlet:namespace />fm" onSubmit="<portlet:namespace />saveGroup(); return false;"> In addition, we need to make the button Add Community available, in the following manner: Locate JSP file group_search.jsp in the /portal/portal-web/docroot/html/portlet/enterprise_admin folder. Copy the JSP file group_search.jsp from /portal/portal-web/docroot/html/portlet/enterprise_admin to /ext/ext-web/docroot/html/portlet/ext/communities, and open it. Update /communities/edit_community with /ext/communities/edit_community as shown in the following lines, and save it: submitForm(document.<portlet:namespace />fm, '<portlet:renderURL windowState="<%= WindowState.MAXIMIZED.toString() %>"> <portlet:param name="struts_action" value="/ext/communities/edit_community" /> <portlet:param name="redirect" value="<%= currentURL %>" /> </portlet:renderURL>'); Congratulations! You have cloned the Communities portlet. Finally, we can deploy updates into Tomcat as follows: Stop Tomcat if it is running. Click on the Ant target: deploy at the Ant view ext. Start Tomcat. Open up a new browser with the URL http://localhost:8080. Click on Sign in and enter test@liferay.com / test. Click on Add Application | Book
Read more
  • 0
  • 0
  • 1738

article-image-customizing-plugins-joomla-15x-part-2
Packt
21 Oct 2009
8 min read
Save for later

Customizing Plugins in Joomla! 1.5x (Part 2)

Packt
21 Oct 2009
8 min read
Step 2: Plan out our changes Just like with our module, we are going to be systematic about our customization. This keeps us organized and reduces the chances for mistakes. Really, these changes are so simple we could probably just dive in and do them, but we want to build good habits for when we want to customize more complex extensions. Step 2.1: Decide on our changes Our plugin is going to be essentially the same, hiding or showing parts of our content depending on a particular condition. Only we want to change it so the condition we use is user's subscription and not their user group. We will need to put in some code to search the database for the visitor's subscription information. We also want to clean out any code we don't need, such as the description HTML page and images. We will go a little bit further and rename our extension and functions. One day we may want to distribute this plugin to get some traffic to our site, and help other developers like ourselves. Also, seeing as we are going to rebuild most of this plugin, let's put a short description in to remind us what it is for, or in case we hire another developer to help with our site later, they can see what it does. Step 2.2: Mark out our changes Remember that before we actually make our changes, we want to go through the code and mark them with comments first. This way we are forced to think the whole process through from start to finish before we write any code, and we can see any potential problems before they happen. This beats finding them after we have spent a few hours writing code, and wasting that time going back to repair them. en-GB.plg_content_njaccess.ini First, we are going to edit our language file, en-GB.plg_content_njaccess.ini. If we were making a complex component, we would usually keep the language file open the entire time, and add new entries to it every time we wanted to put some text onto the screen. But our plugin is pretty much a 'behind the scenes' plugin so we don't need much text. So what text do we need? Well, as we discussed above, when we hide some content from a user, we probably want to display a message that tells them that it has been hidden, and that they should get a subscription to read it. We also want to remove the current rich description and replace it with simpler, normal text. So let's add a note to our current code, NINJACONTENT=<IFRAME SRC="../plugins/content/njaccess/njaccess_desc.html" WIDTH=600 HEIGHT=600 FRAMEBORDER=0 SCROLLING=yes></IFRAME> that tells us to delete it completely. Then we will add a note to write our description and message in its place. # TODO-Remove thisNINJACONTENT=<IFRAME SRC="../plugins/content/njaccess/njaccess_desc.html" WIDTH=600 HEIGHT=600 FRAMEBORDER=0 SCROLLING=yes></IFRAME># TODO-Add plain text description# TODO-Add message for hidden text Wait a minute! What are these hashes? We haven't seen them before. Up until now we were told that comments were either double slashes (//), enclosing slash asterisks (/* … */), or for HTML some long tags (<!-- … -->). Well, .ini files are different from our .php files, and are processed differently. As a result, they use a different symbol to indicate for comments. So now, we can add # to our list of comment symbols, but for .ini (language) files only. njaccess.php Next, open up njaccess.php. As we are basically re-writing this plugin, we might as well change the name of this file and all the functions to something more relevant. // TODO-Rename this file// Ensure this file is being included by a parent file. defined('_JEXEC') or die( "Direct Access Is Not Allowed" );jimport('joomla.eventplugin');// TODO- Rename the class to match our new filenameclass plgContentNJaccess extends JPlugin {// TODO- Rename this constructorfunction plgContentNJaccess( &$subject ){... We don't have any parameters, so we can remove the parameter loading from the constructor. ...parent::__construct( $subject );// TODO-Remove these as we have no need for parameters$this->_plugin = JPluginHelper::getPlugin( 'Content','ninjaacess' );$this->_params = new JParameter( $this->_plugin->params );} We are renaming everything, so we should rename our regex tags and the function call via preg_replace_callback as well. function onPrepareContent(&$article, &$params, $limitstart) {// TODO- Adjust our regex to look for a shorter tag// and one collector function between the tags$regex = "#{njaccess(.*?)}(.*?){/njaccess}#s";// TODO- Rename the function call$article->text = preg_replace_callback($regex,array($this,"njaccess"), $article->text);return true;}// TODO- Rename the functionfunction njaccess(&$matches) { We also want to remove any references to the ACL. We do want to continue to load the user information though, as we need their user id (if logged in) to compare it to the subscriptions in the AEC tables. $user = &JFactory::getUser();// TODO- Remove the next 3 lines as we don't need ACL$acl = &JFactory::getACL();$myRealGid = intval( $acl->get_group_id( $user->usertype ) );$accessLevels = ''; We are only going to have one collector pattern now, so only one set of information, the text to be shown/hidden, needs to be collected. To do this, we need to change all the references to $matches[2] into $matches[1] and remove the old $matches[1] checks. // TODO-change this to matches[1] as we only have// one collector now$output= $matches[2];// TODO-Remove thisif (@$matches[1]) {$accessLevels = explode(",", trim($matches[1]));} Lastly, we need to replace the main processing with a query to check our visitor's user id against the AEC subscription tables for an active paying subscription. // TODO-Replace this with a query searching for the// user's id in the subscriptions table, searching// for a paying subscriptionif (in_array($myRealGid,$accessLevels))return $output;// TODO- Get the visitor's id if available.// If a guest (id = 0) then skip this and display// the please subscribe message// TODO- Look for the id in the AEC subscriptions// table, and check if they have a valid, paid// subscription. If so, return the text// if not, skip it and return the message// TODO- Instead of blank, return our message from our// language filereturn "";}} njaccess.xml Finally, we come to our njaccess.xml file. Comments can be made into XML files in the same way as HTML <!-- … -->. For our XML manifest, we have a few things to do. At first, we want to rename everything from njaccess, including the manifest itself. <?xml version="1.0" encoding="utf-8"?><install version="1.5" type="plugin" group="content"><!-- TODO- Rename this file and plugin --><name>Ninja Access</name><author>Daniel Chapman</author><creationDate>February 2008</creationDate><copyright>(C) 2008 Ninja Forge</copyright><license>http://www.gnu.org/copyleft/gpl.html GNU/GPL</license><authorEmail>daniel@ninjaforge.com</authorEmail><authorUrl>www.ninjaforge.com</authorUrl> Also, let's change the version number of our new plugin to 1.0. Then change the description as well, to suit what we put into our language file. <!-- TODO- Change to 1.0 --><version>1.1</version><!-- TODO- Change to match our language file --><description>NINJACONTENT</description> Then, we want to remove all the unnecessary files from the description <!-- TODO- Remove unneeded files --><files><filename plugin="njaccess">njaccess.php</filename><filename>njaccess/njaccess_desc.html</filename><filename>njaccess/js/ninja.js</filename><filename>njaccess/images/logo.jpg</filename><filename>njaccess/images/ninjoomla.png</filename><filename>njaccess/images/firefox2.gif</filename><filename>njaccess/images/jcda.png</filename><filename>njaccess/images/validation_xhtml.png</filename><filename>njaccess/images/validation_css.png</filename><filename>njaccess/images/info.png</filename><filename>njaccess/images/change.png</filename><filename>njaccess/images/inst.png</filename><filename>njaccess/images/tabbg.gif</filename><filename>njaccess/images/tab2.png</filename><filename>njaccess/images/gnugpl.png</filename></files> Finally, rename the reference to our language file to suit the new filename: <params></params><!-- TODO- Rename the language file --><languages><language tag="en-GB">en-GB.plg_content_njaccess.ini</language></languages></install>
Read more
  • 0
  • 0
  • 1662

article-image-creating-pseudo-3d-imagery-gimp-part-1
Packt
21 Oct 2009
9 min read
Save for later

Creating Pseudo-3D Imagery with GIMP: Part 1

Packt
21 Oct 2009
9 min read
In the previous article I've written ( Creating Convincing Images with Blender Internal Renderer-part1), I discussed about creating convincing 3D still images through color manipulation, proper shadowing, minimal lighting, and a bit of post-processing, all using but one application – Blender. This time, the article you're about to read will give us some thoughts on how to mimic a 3D scene with the use of some basic 2D tools. Here again, I would stress that nothing beats a properly planned image, that applies to all genres you can think of. Some might think it's a waste of precious time to start sitting and planning without having a concrete output at the end of the thought process. But believe me, the ideas you planned will be far more powerful and beautiful than those ideas you just had, when you were just messing around and playing with the tool directly. In this article, I wouldn't be teaching you how to paint since I'm not good at it, rather I'll be leading you through a series of steps on how to digitally sketch/draw your scenes, give them subtle color shifts, add fake lighting, and apply filter effects to further emulate how 3D does its job. Primarily, this all leads you into a guide on how I create my digital drawings (though I admit they're not the best of its kind), but somehow I'm very proud, I eventually gave life to them from concept stage to digital art stage. It might be a bit daunting at first, but as you go along the series, you'll notice it gets simpler.  However, some might get confused as to how this applies to other applications since we're focusing on The GIMP in this article. That's not a problem at all once you are familiar with your own tool; it will just be a matter of working around the tools and options. I have been using The GIMP for a long time already, and as far as I can remember, I haven't complained on its shortcomings since those shortcomings are only but bits of features which I wouldn't be need at all.  So to those of you who have been and are using other image editing programs like Adobe Photoshop, Corel, etc., you're welcome to wander around and feel free to interpret some GIMP tools to that of yours.  It's all the same after all, just a tad bit difference on the interface. Just like what Jeremy Birn has said on one of his books: “Being an expert user of a 3D Program, bit itself, does not make the user into an artist more than learning to run a Word Processor makes someone into a good writer.” Additionally, one vital skill you have to develop is the skill of observation, which I myself ham yet to master. Methods Used Basic Drawing Selection Addition, Subtraction, Intersection Gradient Coloring Color Mixing Layering Layer Modes Layer Management Using Filters Requirements Latest version of The GIMP (download at http://www.gimp.org/downloads) Basic knowledge of image editing programs with layering capabilities Patience Let's Get Started! I would already assume you have the latest version of GIMP installed on your system and is running properly, otherwise, fix the problem or ask help from the forum (http://www.gimptalk.com). I'm also assuming you have all your previous tasks done before sitting down and going over this article (which I'm pretty much positive you are). And then lastly, be patient. Sketch it out The very first thing we're going to do is to sketch our ideas for the image, much like a single panel of a storyboard. It doesn't matter how good you draw it as long as you understand it yourself and you know what's going on in the drawing. This time, you can already visualize and create a picture of your final output and it's great if you did, if not, it's fine still. The important thing is we have laid down our scene one way or another. You can take your time sketching out your scenes and adding details to them like how many objects are seen, how many are in focus, what colors do they represent, how are your characters' facial expressions, what is the size of your image, etc. So just in case we forgot how it's going to look like in the end, we have a reference to call upon and that is your initial sketch. This way, you'll also be affected by the persistence of vision where after hours and hours (yay!) of looking on your sketch, you somehow see an afterimage of what you are about to create, and that's a good thing! I'm not good at sketching so please bear with my drawing: After this, it's now time to open up The GIMP and begin the actual fun part! First Run After executing GIMP, this should (and most likely) be the initial screen that's going to be displayed on your screen: The GIMP Initial Screen We don't want Wilbur (GIMP's Mascot) to be glaring at us from a blank empty window all the time, do we? And right now we could go ahead and add a canvas with which we'll be adding our aesthetic elements into, but before that you might want to inspect your application and tool preferences just to make sure you have set everything right. Activate the window with the menu bar at the top (since we currently have three windows to choose from), and then locate Edit > Preferences, as seen below: Locating GIMP's Preferences GIMP Preferences Everything you see here should be self-explanatory, if it isn't, just leave it for the moment and check the manual later, since I'm pretty much sure that thing you didn't understand on the Preferences must be something we will not use here.  So go ahead and save whatever changes you did and sometimes, GIMP might ask you to restart the application for the changes to take effect, then do what she says and we should be back on the black canvas shortly after application restart. By now, we should be having three windows, the main Toolbox Window (located on the left), the main Image Window (located on the middle), and the Layers Window (located on the right).  If, by any chance, the Layer Window is not there, go ahead and activate the Image Window and go to Windows > Layers or press CTRL + L to bring up the Layer Window. Showing the Layers Window Creating the Canvas Now that everything's set up, we'll go ahead and add a properly-sized canvas that we'll paint on, which will be the entire universe for our creation at a later stage. Let's go and create than now by going to File > New or by pressing CTRL+ N. A window will pop up asking you to edit and confirm the image settings for the canvas you're creating. You can choose from a variety of templates to use or you can manually input sizes (which we are going to do).  Before that, change the unit for coordinate display to inches just so we could have a better visual reference of how big our drawing canvass will be. Then on the Width input box, type 9 (for nine inches), and for the Height input box, type 6 (for six inches) respectively. This, however, is a very subjective portion, since you can just have any size you prefer, I just chose nine inches by six inches for the purposes of this article. Clicking the Advanced Options drop-down menu will reveal more options for you.  But right now, we'll never deal with that, just the width and height are sufficient for what we'll be need. When you're done setting up the dimensions and settings, click OK to confirm (is there a chance we could chance the OK buttons to “Alright” buttons, which sounds, uhmmm, better). Creating a New Image At this moment, we should be seeing a blank canvas with the dimensions that we've set awhile back. Then just at the right window (Layers Window), you'll notice there's already one layer present as compared to the default which is none. So everytime we add a new layer (which is very vital), we'll be referencing them over to the Layers Window. Since the creation of the layering system in image editors, it has been a blast to organize elements of an image and apply special effects on them as necessary. We can imagine layers as transparent sheets overlaying each other to form one final image; one transparent sheet can have a landscape drawn, another sheet contains trees and vegetation, and another sheet (which is above the tree sheets) is our main character. So together, we see a character with trees on a landscape in one. But as far as traditional layering is concerned, digital layering has been far more superior in terms of flexibility and the amount of modes we can experiment with. New Image with Layer This time might be a good idea to save our file natively, by that I mean save it in a format that is recognizable only by GIMP and that is lossless in format, so whether we save it a couple of times as such, no image compression happens and the image quality is not compromised. However, the native format is only related to GIMP and is not known elsewhere, so uploading such file to your website will show no image at all because it isn't recognized by the browser. In order to make it generally compatible, we export our image to known formats like JPEG, PNG, GIF, etc. depending on your need. Saving an image file on its native format preserves all the options we have like selections, paths, layers, layer modes, palettes, and many more. This native format that GIMP uses is known as .XCF which stands for “eXperimental Computing Facility”. Throughout this article, we'll save our files mainly in .xcf format and later on, when our tasks are done and we call our image finished, that's the time we export it to a readable and viewable format. Let's go ahead and save our file by going to File > Save, or by pressing CTRL + S. This brings up a window that we can type our filename into and browse and create the location for our files. Type whatever filename you wish and append the “.xcf” file extension at the end of the filename, or you can choose “GIMP xcf image” from a list on the lower half of the window. Saving an Image as XCF
Read more
  • 0
  • 1
  • 7704

article-image-managing-content-through-tagging-grails-part-2
Packt
21 Oct 2009
10 min read
Save for later

Managing Content through Tagging in Grails: Part 2

Packt
21 Oct 2009
10 min read
Customizing the home page in Grails With tagging in place, we can enhance the application to allow users to create their own home page. The aim is to allow users to specify the tags they are interested in, so any content with these tags will be displayed on their home page. This will allow us to break the home page up into two sections: A Most Recent section, containing the last five file uploads and messages A Your Data section, containing all the files and messages that are tagged according to the user's preferences Introducing templates Taking this approach means that files and messages will be displayed in many different places on the site, instead of just the home page. By the end of this article, messages and files will be rendered in the context of: A Most Recent section A Your Data section In the future, we will probably render messages and files in the following contexts as well: Show all files and messages Show files and messages by tags Show files and messages by search results Ideally we want to encapsulate the rendering of a file and a message so they look the same all over the site, and we don't need to duplicate our presentation logic. Grails provides a mechanism to handle this, through GSP, called templates. A template is a GSP file, just the same as our view GSP files, but is differentiated from a view by prefixing the file name with an underscore. We are going to create two templates—one template for messages, which will be called _message.gsp and the other for files, which will be called _file.gsp. The templates will be responsible for rendering a single message and a single file. Templates can be created anywhere under the views folder. The location that they are created in affects the way they are executed. To execute a template we use the grails render tag. Assume that we create our message template under the views/message folder. To render this template from a view in the same folder, we would call the following: <g:render template="message" /> However, if we need to render a message from another controller view, say the home page, which exists under views/home, we would need to call it like so: <g:render template="/message/message" /> Passing data to a template The two examples of executing a template above would only be capable of rendering static information. We have not supplied any data to the template to render. There are three ways of passing data into a template: Send a map of the data into the template to be rendered Provide an object for the template to render Provide a collection of objects for the template to render Render a map This mechanism is the same as when a controller provides a model for a view to render. The keys of the map will be the variable names that the values of the map are bound to within the template. Calling the render tag given below: <g:render template="message" model="[message: myMessage]" /> would bind the myMessage object into a message variable in the template scope and the template could perform the following: <div class="messagetitle"> <g:message code="${message.title}" encodeAs="HTML"/></div> Render an object A single object can be rendered by using the bean attribute: <g:render template="message" bean="${message}" /> The bean is bound into the template scope with the default variable named it: <div class="messagetitle"> <g:message code="${it.title}" encodeAs="HTML"/></div> Render a collection A collection of objects can be rendered by using the collection and var attributes: <g:render template="message" var="message" collection="${messages}" /> When using a collection, the render tag will iterate over the items in the collection and execute the template for each item, binding the current item into the variable name supplied by the var attribute. <div class="messagetitle"> <g:message code="${message.title}" encodeAs="HTML"/></div> Be careful to pass in the actual collection by using ${}. If just the name of the variable is passed through, then the characters in the collection variable name provided will be iterated over, rather than the items in the collection. For example, if we use the following code, the messages collection will be iterated over: <g:render template="message" var="message" collection="${messages}" /> However, if we forget to reference the messages object and just pass through the name of the object, we will end up iterating over the string "messages": <g:render template="message" var="message" collection="messages" /> Template namespace Grails 1.1 has introduced a template namespace to make rendering of templates even easier. This option only works if the GSP file that renders the template is in the same folder as the template itself. Consider the first example we saw when rendering a template and passing a Map of parameters to be rendered: <g:render template="message" model="[message: myMessage]" /> Using the template namespace, this code would be simplified as follows: <tmpl:message message="${myMessage}"/> As we can see, this is a much simpler syntax. Do remember though that this option is only available when the GSP is in the same folder as the template. Create the message and file templates Now, we must extract the presentation logic on the home page, views/home/index.gsp, to a message and file template. This will make the home page much simpler and allow us to easily create other views that can render messages and files. Create two new template files: /views/message/_message.gsp /views/file/_file.gsp Taking the code from the index page, we can fill in _message.gsp as follows: <div class="amessage"> <div class="messagetitle"> <g:message code="message.title" args="${[message.title]}" encodeAs="HTML"/> </div> <div class="tagcontainer"> <g:message code="tags.display" args="${[message.tagsAsString]}" /> </div> <div class="messagetitlesupplimentary"> <g:message code="message.user" args="${[message.user.firstName, message.user. lastName]}"/> </div> <div class="messagebody"> <g:message code="message.detail" args="${[message.detail]}" encodeAs="HTML"/> </div></div> Likewise, the <div> that contains a file panel should be moved over to the new _file.gsp. This means the main content of our home page (views/home/index.gsp) becomes much simpler: <div class="panel"> <h2>Messages</h2> <g:render template="/message/message" collection="${messages}" var="message"/></div><div class="panel"> <h2>Files</h2> <g:render template="/file/file" collection="${files}" var="file"/></div> User tags The next step is to allow users to register their interest in tags. Once we have captured this information then we can start to personalize the home page. This is going to be surprisingly simple, although it sounds like a lot! We just need to: Create a relationship between Users and Tags Create a controller to handle user profiles Create a form that will allow users to specify the tags in which they are interested User to tag relationship Creating a relationship between users and tags is very simple. Users will select a number of tags that they want to watch, but users themselves are not 'tagged', so the User class cannot extend the Taggable class. Otherwise users would be returned when performing a polymorphic query on Taggable for all objects with a certain tag. Besides allowing a user to have a number of tags, it is also necessary to be able to add tags to a user by specifying a space delimited string. We must also be able to return the list of tags as a space delimited string. The updates to the user class are: package appimport tagging.Taggerclass User { def tagService static hasMany = [watchedTags: Tagger] ... def overrideTags( String tags ) { watchedTags?.each { tag -> tag.delete() } watchedTags = [] watchedTags.addAll( tagService.createTagRelationships( tags )) } def getTagsAsString() { return ((watchedTags)?:[]).join(' ') }} User ProfileController The ProfileController is responsible for loading the current user for the MyTags form, and then saving the tags that have been entered about the user. Create a new controller class called ProfileController.groovy under the grails-app/controller/app folder, and add the following code to it: package appclass ProfileController { def userService def myTags = { return ['user': userService.getAuthenticatedUser() ] } def saveTags = { User.get(params.id)?.overrideTags( params.tags ) redirect( controller:'home' ) }} The myTags action uses userService to retrieve the details of the user making the request and returns this to the myTags view. Remember, if no view is specified, Grails will default to the view with the same name of the action. The saveTags action overrides the existing user tags with the newly submitted tags The myTags form The last step is to create the form view that will allow users to specify the tags they would like to watch. We will create a GSP view to match the myTags action in ProfileController. Create the folder grails-app/views/profile and then create a new file myTags.gsp and give it the following markup: <%@ page contentType="text/html;charset=UTF-8" %><html><head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta name="layout" content="main"/> <title>My Tags</title></head><body><g:form action="saveTags"> <g:hiddenField name="id" value="${user.id}"/> <fieldset> <dl> <dt>My Tags</dt> <dd><g:textField name="tags" value="${user.tagsAsString}" size="35" class="bigfield"/></dd> </dl> </fieldset> <g:submitButton name="Save" value="Save"/> | <g:link controller="home">Cancel</g:link></g:form></body></html> This view will be rendered by the myTags action on the ProfileController and is provided with a User instance. The form submits the tags to the saveTags action on the ProfileController. The user id is put in a hidden field so we know which user to add the tags to when the form is submitted, and any existing tags for the user are rendered in the text field via the tagsAsString property. Add a link to the myTags action in the header navigation from our layout in main.gsp: <div id="header"> <jsec:isLoggedIn> <div id="profileActions"> <span class="signout"> <g:link controller="profile" action="myTags">My Tags</g:link> <g:link controller="auth" action="signOut">Sign out</g:link> </span> </div> </jsec:isLoggedIn> <h1><g:link controller="home">Teamwork</g:link></h1></div> Now restart the application, log in as the default user and you will be able to specify which tags you are interested in.
Read more
  • 0
  • 0
  • 1496

article-image-showing-drupals-cck-module-fields-flash
Packt
21 Oct 2009
6 min read
Save for later

Showing Drupal's CCK Module Fields in Flash

Packt
21 Oct 2009
6 min read
Building a Recipe widget in Flash We will start out by first increasing the size of our stage in Flash to 500 x 640. Once we have done this, we will need to resize our background so that it fits to the new stage. We will start this by first selecting the whole background region, and then converting that into a new Movie Clip by selecting Modify | Convert to Symbol from the Flash menu. This will t hen bring up a new dialog, where we can give our new Movie Clip a name, which we will call mcBackground. We then need to make sure that we check the Enable guides for 9-slice scaling, which will allow us to resize the background without affecting the rounded edges. Once we create a new movie clip from our background, we will then enter this Movie Clip and then adjust the 9-scale guides so that they only cover the rounded edges. We can now ex it the background movie clip, and then resize the movie clip to a new height of 632 using the Properties panel. Our next task is to move the current title field to the top-left of our Flash application, and then create some background regions that will hold our new fields. The design of how this will look is completely up to you, but here is an illustration of what I just described: Now that our layout is ready for new content, the next step is to add new TextFields to hold our recipe content. Adding dynamic TextFields for Drupal content The important thing to note here is that we will need to create a new layer for each text element within our Flash application, so that we can keep track of each field separately. We will do this within the timeline by creating three new layers for each of our new fields, and by then labeling them so that we can easily determine what they contain. Now that we have each one of these separated, we can add new text fields in each layer, to be used for the description, ingredients, and the instructions. For each new Text field that we create, we will need to make sure to give them an instance name so that we can reference them within ActionScript. Each of these instance names should reflect the names of the fields that we created for our Recipe content type, which will be description, ingredients, and instructions respectively. When we are done, we should have something that resembles the following: We are now ready to hook up these TextFields to real Drupal content. Using ActionScript to show Drupal CCK fields We can start this off by opening up our main.as file, and then we will shift our focus to the onNodeLoad function. // Called when Drupal returns with our node.function onNodeLoad( node:Object ){ // Print out the node title. title.text = node.title;} This function gets called after our service call to Drupal's node.get service call and returns with the contents of the node. Since we have new TextFields for each custom recipe field, we can use the node object, passed to the onNodeLoad function, to reference the data from these custom fields, and populate our TextFields with that data. Since the contents of this node object are somewhat a mystery, there is a fantastic tool that is provided with Drupal that will allow us to examine how this node is structured. We will then be able to use that information to fill out the contents of our onNodeLoad function to show our complete recipe node. Using the Services Administrator We now need to shift our focus back to Drupal, where we will navigate to the Service Administrator section by going to Administer | Services. The Services module comes equipped with a fantastic tool for analyzing any service routine when working with external applications. It allows for you to call any service routine, with any specified argument, and then see the result of that routine call. This can be used to easily analyze the data structure that our Flash application will receive after it makes a call to any of the service routines available. Since we are using the node.get service routine to load each recipe node, we should be able to examine how the Description, Ingredients, and Instructions fields are represented, and then easily apply that to our Flash application. Let's do this by clicking on the link that says node.get in the node section. This will bring up the following page: The Services module automatically places a valid Session id in the session field, so we can just keep this field as it is. Because of this, all we really need to provide is the nid (node ID) of our Recipe node—since the fields field is optional. In order to determine the node ID for any node within the Drupal web site, simply navigate to Administer | Content, which will list all the content within the Drupal web site. The node ID can be found by hovering over any content link and then reading the last number in the URL. For example, if we hover over our Recipe node, we should see a URL similar to http://localhost/drupal6/node/5, which means that our node ID for this node is 5. After we have the entered the node ID in the nid field, we can now click on the button that says Call Method. This will then show the results of that call within the Results section just below the Call Method button. To the untrained eye, this may look intimidating, but really what this is showing is the results for all the data contained within the recipe node that we just created, including the Ingredients and Instructions. If we look within this data structure, we should see something that looks similar to the following: [field_ingredients] => Array( [0] => Array ( [value] => 1 skinless, boneless chicken breast half 2 tablespoons minced green onion 2 tablespoons minced red bell pepper 3/4 cup shredded Monterey Jack cheese 5 (6 inch) flour tortillas [format] => 1 )) Within our Flash application, we can now access the Ingredients field in the node object (which is what is returned when you call node.get). The ActionScript code to reference this field should look similar to the following: node.field_ingredients[0]["value"] Now, let's apply this concept to show the ingredients and instructions in our Flash application.
Read more
  • 0
  • 0
  • 1964

article-image-putting-sakai-work
Packt
21 Oct 2009
16 min read
Save for later

Putting Sakai to Work

Packt
21 Oct 2009
16 min read
The fundamental determinants of course quality have always been, and remain, the course content, the instructor(s), the learning activities in which the students are engaged, and the students themselves. We do not make any exaggerated promises about the transformative nature of technology in education. Technology like Sakai can be used to improve your course by allowing you and your students to do things that might have been impractical without the technology or by reducing the amount of time spent on administrative issues. But the tools Sakai provides are just that—tools—and unless they are used purposefully they will not make much of a difference. So it is most important for you to consider what you want to accomplish with your students and how the capabilities Sakai provides can support the course activities. There is not a single "best" way of teaching. What works in a small graduate seminar in philosophy may not work in a large introductory computer science class, and what works for one instructor may not work for another. The good news is that Sakai is designed with this variety in mind. The tools and structure of a Sakai site You can think of Sakai as a framework that allows you to create the kind of online experience you want for your students. There is not a single way that a Sakai course site needs to function—it is ultimately up to you, with assistance from your Sakai user support team, to determine how the course is presented to the students online. Not all Sakai sites are used for courses. In many institutions, it is common to find Sakai used for research work groups and administrative collaboration. In fact, at many institutions, there are more "collaboration sites", as they are called in the Sakai community, than there are course sites. So, these types of sites are included in the overview. Many institutions integrate Sakai with other enterprise systems—automatically creating a Sakai site for each course being taught, for example, and adding students who are registered for the course to the site. Other organizations require instructors to create a site online but, once that's created, students are added and removed based on data from the registrar's office. Regardless of the type of course (or collaboration site) you might be teaching, there is a basic structure to Sakai that is useful to understand. This section describes that basic structure and common tools. It also gives you a quick and easy way to customize a Sakai site, so we recommend having a browser window handy with access to an instance of Sakai. If you have not yet created a Sakai site or if you don't have access to an instance of Sakai at your organization, several Sakai Commercial Affiliates host trial versions of Sakai that you can access free of charge. Check www.sakaiproject.org for the current listing. At the time of writing, hosted trials were available from rSmart (http://sakaisandbox.com), Serensoft (http://sakaisthelimit.com), and Unicon (http://testdrivesakai.com). When you create your account, a site is automatically created for you. Sakai's site structure Once you've logged into Sakai the top of the screen consists of a Sakai banner that contains your organization's branding and a logout button. Just below that banner is a series of tabs, one for each Sakai site that you are a member of. Every course or project consists of a Sakai site and is accessed by clicking a tab labeled with the name of the site. If you are a member of more than one site, you have more than one tab across the top of your screen. If you have too many sites to fit, a More Sites menu (often renamed My Sites or My Active Sites) is present; it enables you to access the appropriate site. Always be sure you're in the correct site when you're doing your work. My Workspace There is also a tab called My Workspace, a special site that only you have access to. You use it to manage your preferences, store personal files, manage site memberships, and even provide a calendar that pulls information from all the Sakai sites to which you belong. Contact your local Sakai support team for more information about uses of My Workspace. You can modify which sites appear across the top of the page so that your most frequently accessed sites are a single click away. To do that, go to your My Workspace site and choose the Preferences tool. The Customize Tabs function enables you to change which sites are visible. A basic Sakai site is a collection of tools that users have access to, typically presented to users in a sidebar on the left of the screen. In most Sakai installations, each link in the left sidebar corresponds to a single tool. To access the Assignments tool, for example, a user clicks the Assignments link in the sidebar. The Assignments tool then appears in the main content area of Sakai. In most cases, it is as simple as that. The major exception to this rule is the site Home page. It is a significant exception because it is the first page you see when you access a site. The Home tool contents Unlike most tools, the Home tool is not a tool at all. It is really a summary page. It generally includes information pulled from several different tools. Sakai tries to be helpful by providing the most commonly useful information and, therefore, a typical course site Home page includes areas (called panels) that present the following information: A site description (a description of the course or project) Recent course announcements Recent discussion forum posts Recent chat messages A course calendar Most of the panels on the Home page can be configured directly from the Home page. The Calendar panel, for example, allows you to specify whether the calendar is presented by week or by month. To add events to the Calendar, however, you need to use the Calendar tool itself. The Announcements panel lets you to determine how many recent announcements are shown (you can specify either a certain number of announcements or have the panel display announcements that have been created since a specific number of days in the past). Some panels enable you to edit the content itself. The Site Description panel, for example, provides an Options link that allows you to edit the content directly, obviating the need to access the tool directly. Of course, if you are not planning to use a tool in your site you do not want its information panel to appear on the course Home page. If you are not going to make a chat room available in your course, for instance, you do not want your course Home page to include a summary of recent chat messages because that would make your site look unfinished. Thankfully, Sakai is generally very smart about this—if you turn off the Chat tool in your site, the recent chat messages summary panel on the Home page simply disappears. Except for editing the Course Description content, you can mostly ignore the Home page and let Sakai take care of it for you. To edit the Site Description, simply click on the Options button. There are four things you can do: Specify the title for the panel. A good title reflects the type of site you have (for example changing the default Site Description to Course Description or, better yet, Welcome to Theater 101). Create the content that appears in the body of the panel. An easy-to-use HTML editor enables you to enter whatever content you want. If you don't know HTML, the default WYSIWYG mode can be used like a simple word processor. Just type away using the built-in text styling buttons to add bold, italics, bulleted lists, and so on. You can also add images, hyperlinks, and tables quite easily. If you know HTML, you can use the source code mode to view and edit the HTML directly. Specify the height for the panel. You want to make the panel large enough so there is no scroll bar within the panel itself. This is easy if there is not another panel just below it. If there is, make the panel just large enough to hold your content but not so large that there is excessive white space between the panels. This may take a bit of trial and error. Enter a URL instead of writing content. If you already have an HTML page that describes the site published elsewhere, you can simply enter that web address. Sakai will display that page in the Site Description panel. Then simply click Update Options to save the changes you've made. If you have access to Sakai, this would be a good time to try it out. Remember to ensure you are working in the correct site. There are, certainly, many instructors who want their course Home page to appear in a very specific manner. There are risks to that level of customization—remember that your students are likely taking several courses at one time and they may benefit from a consistent Home page experience. Nonetheless, it is often quite appropriate to create a custom Home page. Later, you'll see a number of ways to significantly customize the appearance of your site, including the Home page. First, though, let's review tools to make available and just let the Sakai Home page do most of the work. The basic collaboration tools There are four basic tools that almost every Sakai site uses, regardless of whether it is a course or a collaboration site: Resources. The tool for storing files that can be accessed by other members of the site. You can create folders to organize these files, and even make certain files publicly viewable (such as a web page you want to show to those who aren't part of your site). Announcements. Use it to send announcements to members of the site. These announcements appear on the site Home page and can be sent to all site members via email. Email Archive. This tool provides a dedicated email address for your site (mysitename@sakai.myschool.edu, for instance). While permissions can be configured to make Sakai behave differently, typically members of your site can send email to this address and everyone belonging to the site will get the email. All emails are archived and accessible using the Email Archive tool. Calendar. Sometimes called Schedule, the Calendar tool allows you to put important events on a calendar. The Sakai Calendar supports recurring events and has different icons for different types of events such as class meetings, exams, and special events. Site administration As a site owner, there are certain tasks that you may want to undertake to modify the site and/or the members of the site. The Site Info tool allows you to: Modify the tools available in your site, including modifying the order in which those tools appear in the toolbar and changing the names of the tools. Manage access to the site, including specifying whether individuals can join your site without your approval. Manage the membership of your site. What you can and cannot do with membership depends on your local Sakai installation and often varies based on organizational policies, but typically includes the capability to add and remove members and change the role of individual members. Manage groups of users in your site. Many Sakai tools have special features that allow you to work with a particular set of users inside your site. Groups and class sections are good examples of predefined groups that may be automatically created by your Sakai administrators. You can also create ad hoc groups for particular purposes. If you have project teams in your course, for example, it might be useful to create a group for each team. Import content from an existing site or export content from the current site to a new one. As a site owner, you always have access to the Site Info tool. Depending on how you use Sakai and how Sakai is integrated with other systems on your campus, you may never use Site Info. Still, you should take a quick look to familiarize yourself with what is available to you. Every Sakai installation is different from every other. Your organization may not make the same Sakai tools available as another organization. This article mainly restricts itself to those tools that will be commonly available in installations of Sakai version 2.6.x, but there may be instances where we mention a tool that is not available in your instance of Sakai. In many cases, you can request these tools from your Sakai administrator. We also try to mention alternatives to the recommended tool where they exist. The basic teaching and learning tools In addition to the basic collaboration and administration tools, there are three tools that are commonly used in Sakai Course sites: Syllabus, Assignments, and Gradebook. Syllabus. Use this tool to put your syllabus (clear guidelines and expectations for your course) online. You can upload a document (such as a Microsoft Word document or a PDF), build a structured syllabus in Sakai, or even point to an existing syllabus you have posted online in another location. Regardless of how you use the tool, you can have Sakai automatically email students when you've made a change to the syllabus. Assignments. This tool allows you to create and post assignments that students can submit electronically. Using the tool can help eliminate paper assignments and reduce class time spent collecting and returning student work. It allows students to send questions about assignments and enables you to post online comments, grade assignments, and transfer grades to the Gradebook automatically. The tool lets you set opening and closing times for each assignment, supports resubmissions, and marks each student submission with a date and time stamp. Gradebook. The Gradebook tool allows instructors to record and compute cumulative student grades. Students can refer to the Gradebook to check their progress in a course. The Gradebook tool is often used hand-in-hand with the Assignments tool although they can be used separately. With these three tools and the four basic collaboration tools reviewed earlier, you can create a solid online presence for your class. Students will get course announcements and can send and receive emails via the class email address and can check past messages online. You can post reading material and other resources for students online and build a course calendar to remind students of important events and deadlines. Your syllabus is available online and students are automatically updated with any modifications to it. You've provided a facility for students to submit their assignments electronically, and they can receive feedback on those assignments via Sakai as well. Their grades are computed online and they can check to see how they are doing at any time. Now that you have your course's online infrastructure set up, we can begin to add some more sophisticated uses of online tools. We'll do this by turning our attention to the several common types of courses and discuss how a Sakai site can be structured to support each type of course. Types of Sakai sites There are more than four thousand universities and colleges in the United States alone. Each of these teaches hundreds or even thousands of classes every year. Trying to create some structure from such a diverse world would be an exercise in oversimplification. The categories discussed here along with the associated recommendations about how to support them in Sakai are not meant to be rules or even best practices but rather a place to start when thinking about structuring your Sakai site. Do read through all of the site types because it is likely that your course mixes the structures and activities in two or more site types. And your own personal comfort with technology will also determine how many (and which) tools you might want to use. The site types you'll be working with are as follows: Problem-based courses Small discussion courses Large, introductory courses Project-based courses Collaboration sites The following sections highlight a small number of tools that are especially useful in a site used for that purpose. Other tools are often useful as well, but because you're just starting out, you'll have more success using a few tools well. Still, we encourage you to read through all the class types and mix and match the tools you feel will work best for your situation. Problem-based courses A problem-based course presents learners with one or more problems to solve on a regular basis (weekly, for instance). The problems are presented by the instructor and generally have either correct answers (such as a Calculus problem set) or at least a fairly clear way to distinguish better solutions (such as an analysis of a poem in an English literature class). Problem-based courses are often targeted at developing skills related to the topic at hand and students are therefore asked to apply what they've learned by solving problems. Math courses might include weekly problem sets, computer science courses might include small programming assignments, creative writing classes may have weekly writing exercises, an acting class may have a series of small scene studies—the general approach is to increase the level of student skill through repeated practice with frequent feedback. The tools introduced so far serve this type of course very well, with a special emphasis on the Assignments and Resources tools, and with the addition of just a few extra tools, you can make the online aspect of your problem-based class very effective: Assignments. Use this tool to enter all or most of the term's assignments at the beginning of the term. That provides students with an excellent guide to the weeks ahead and helps them plan their time accordingly. Resources. For problems with correct answers, use this tool to share sample problems and solutions to past problem sets. For classes where high-quality answers are less well defined (such as a performing arts or product design class), share samples of previous classes' best work. You can even upload video or audio recordings. By providing examples of excellent solutions to open-ended problems, you help your students understand what you're looking for. Forums. This tool provides online discussion that allows students to help each other with difficult problems. You can set up a forum for each problem set. (Be sure to monitor the forum to ensure that students are giving each other good advice and aren't crossing the boundaries by helping too much.) Chat. Use this tool to provide online problem-set help two nights before an assignment is due (don't encourage procrastination by scheduling it the night before the assignment is due). From the comfort of your home, you can take student questions about the assignment and suggest resources they might refer to while working on it.
Read more
  • 0
  • 0
  • 3694
Unlock access to the largest independent learning library in Tech for FREE!
Get unlimited access to 7500+ expert-authored eBooks and video courses covering every tech area you can think of.
Renews at €18.99/month. Cancel anytime
article-image-authentication-and-authorization-modx
Packt
20 Oct 2009
1 min read
Save for later

Authentication and Authorization in MODx

Packt
20 Oct 2009
1 min read
It is vital to keep this distinction in mind to be able to understand the complexities explained in this article. You will also learn how MODx allows grouping of documents, users, and permissions. Create web users Let us start by creating a web user. Web users are users who can access restricted document groups in the web site frontend; they do not have Manager access. Web users can identify themselves at login by using login forms. They are allowed to log in from the user page, but they cannot log in using the Manager interface. To create a web user, perform the following steps: Click on the Web Users menu item in the Security menu. Click on New Web User. Fill in the fields with the following information: Field Name Value Username samira Password samira123 Email Address xyz@configurelater.com    
Read more
  • 0
  • 0
  • 2941

article-image-building-web-service-driven-application-flash-drupal
Packt
20 Oct 2009
6 min read
Save for later

Building a Web Service-driven Application with Flash in Drupal

Packt
20 Oct 2009
6 min read
So, let's take a step-by-step approach on how to accomplish this on the Flash side, which as far as I am concerned, is the fun side! Click here to access all the codes used in this article. Step 1: Creating our Flash application With our chapter2 project open, we can shift our focus to the Actions panel within the Flash IDE. Although working with the Actions panel is great for small applications, we will eventually build onto this Flash application, which might make it impractical to keep all of our ActionScript code within the Actions panel. Because of this, we will first need to create a separate ActionScript file that will serve as our main entry point for our Flash application. This will allow us to easily expand our application and add to the functionality without modifying the Actions panel for every addition we make. Step 2: Creating a main.as ActionScript file For this step, we will simply create an empty file next to our chapter2.fla file called main.as. After you have created this new file, we will then need to reference it within our Actions panel. To do this, we will use the include keyword in ActionScript to include this file as the main entry point for our application. So, shifting our focus back to the chapter2.fla file, we will then place the following code within the Actions panel: include "main.as";stop(); Now that we are referencing the main.as file for any of the ActionScript functionality, we will no longer need to worry about the Actions panel and add any new functionality directly to the main.as file. Now, for the following sections, we will use this main.as file to place all of our ActionScript code that will connect and extract information from our Drupal system, and then populate that information in a TextField that we will create later. So, let's jump right in and write some code that connects us with our Drupal system. Step 3: Connecting to Drupal For this step, we will first need to open up our empty main.as file so that we can add custom functionality to our Flash application. With this file open in our Flash IDE, our first task will be to connect with Drupal. Connecting to Drupal will require us to make a remote call to our Drupal installation, and then handle its response correctly. This will require the use of asynchronous programming techniques along with some standard remoting classes built into the ActionScript 3 library. I will spend some time here discussing the class used by ActionScript 3 to achieve remote communication. This class is called NetConnection. Using the NetConnection class The NetConnection class in ActionScript 3 is specifically used to achieve remote procedure calls within a Flash application. Luckily, this class is pretty straight forward and does not have a huge learning curve on understanding how to utilize it for communicating with Drupal. Using this class requires that we first create an instance of this class as an object, and then initialize that object with the proper settings for our communication. But let's tackle the creation first, which will look something like this in our main.as file: // Declare our Drupal connectionvar drupal:NetConnection = new NetConnection(); Now, you probably noticed that I decided to name my instance of this net connection drupal. The reason for this is to make it very clear that any place in our Flash application where we would like to interact with Drupal, we will do so by simply using our drupal NetConnection object. But before we use this connection, we must first specify what type of connection we will be using. In any NetConnection object, we can do this by providing a value for the variable objectEncoding . This variable lets the connection know how to structure the XML format when communicating back and forth between Flash and Drupal. Currently, there are only two types of encoding to choose from: AMF0 or AMF3. AMF0 is used for ActionScript versions less than 3, while AMF3 is used for ActionScript 3. ActionScript 1 and 2 are much less efficient than version 3, so it is highly recommended to use ActionScript 3 over 1 or 2. Since we are using ActionScript 3, we will need to use the AMF3 format, and we can provide this as follows: // Declare our Drupal connectionvar drupal:NetConnection = new NetConnection();drupal.objectEncoding = ObjectEncoding.AMF3; Now that we have an instance ready to go, our first task will be to connect to the Drupal gateway that we set up in the previous section. Connecting to a remote gateway Connecting to a remote gateway can be performed using the connect command on our drupal NetConnection object. But in order for us to connect, we must first determine the correct gateway URL to pass to this function. We can find this by going back to our Drupal installation and navigating to Administer | Services. In the Browse section, you will see a link to the servers available for remote procedure calls as shown in the following screenshot: For every listed server, we can click on each link to verify that the server is ready for communication. Let's do this by clicking on the link for AMFPHP, which should then bring up a page to let us know that our AMFPHP gateway is installed properly. We can also use this page to determine our AMFPHP gateway location, since it is the URL of this page. By observing the path of this page, we can add our AMFPHP server to our main.as file by combining the base URL of our site and then adding the AMFPHP services gateway to that base. // Declare our baseURL and gateway string.var baseURL:String = "http://localhost/drupal6";var gateway:String = baseURL + "/services/amfphp";// Declare our Drupal connectionvar drupal:NetConnection = new NetConnection();drupal.objectEncoding = ObjectEncoding.AMF3;// Connect to the Drupal gatewaydrupal.connect( gateway ); It is important to note that the connect routine is synchronous, which means that once this function is called, we can immediately start using that connection. However, any remote procedure call that we make afterwards, will be asynchronous, and will need to be handled as such. The function that can be used to make these remote procedure calls to Drupal is called call.
Read more
  • 0
  • 0
  • 2319

article-image-dwr-java-ajax-user-interface-basic-elements-part-2
Packt
20 Oct 2009
21 min read
Save for later

DWR Java AJAX User Interface: Basic Elements (Part 2)

Packt
20 Oct 2009
21 min read
Implementing Tables and Lists The first actual sample is very common in applications: tables and lists. In this sample, the table is populated using the DWR utility functions, and a remoted Java class. The sample code also shows how DWR is used to do inline table editing. When a table cell is double-clicked, an edit box opens, and it is used to save new cell data. The sample will have country data in a CSV file: country Name, Long Name, two-letter Code, Capital, and user-defined Notes. The user interface for the table sample appears as shown in the following screenshot: Server Code for Tables and Lists The first thing to do is to get the country data. Country data is in a CSV file (named countries.csv and located in the samples Java package). The following is an excerpt of the content of the CSV file (data is from http://www.state.gov ). Short-form name,Long-form name,FIPS Code,CapitalAfghanistan,Islamic Republic of Afghanistan,AF,KabulAlbania,Republic of Albania,AL,TiranaAlgeria,People's Democratic Republic of Algeria,AG,AlgiersAndorra,Principality of Andorra,AN,Andorra la VellaAngola,Republic of Angola,AO,LuandaAntigua andBarbuda,(no long-form name),AC,Saint John'sArgentina,Argentine Republic,AR,Buenos AiresArmenia,Republic of Armenia,AM,Yerevan The CSV file is read each time a client requests country data. Although this is not very efficient, it is good enough here. Other alternatives include an in-memory cache or a real database such as Apache Derby or IBM DB2. As an example, we have created a CountryDB class that is used to read and write the country CSV. We also have another class, DBUtils, which has some helper methods. The DBUtils code is as follows: package samples;import java.io.BufferedReader;import java.io.File;import java.io.FileReader;import java.io.FileWriter;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.io.PrintWriter;import java.util.List;import java.util.Vector;public class DBUtils { private String fileName=null; public void initFileDB(String fileName) { this.fileName=fileName; // copy csv file to bin-directory, for easy // file access File countriesFile = new File(fileName); if (!countriesFile.exists()) { try { List<String> countries = getCSVStrings(null); PrintWriter pw; pw = new PrintWriter(new FileWriter(countriesFile)); for (String country : countries) { pw.println(country); } pw.close(); } catch (IOException e) { e.printStackTrace(); } } } protected List<String> getCSVStrings(String letter) { List<String> csvData = new Vector<String>(); try { File csvFile = new File(fileName); BufferedReader br = null; if(csvFile.exists()) { br=new BufferedReader(new FileReader(csvFile)); } else { InputStream is = this.getClass().getClassLoader() .getResourceAsStream("samples/"+fileName); br=new BufferedReader(new InputStreamReader(is)); br.readLine(); } for (String line = br.readLine(); line != null; line = br.readLine()) { if (letter == null || (letter != null && line.startsWith(letter))) { csvData.add(line); } } br.close(); } catch (IOException ioe) { ioe.printStackTrace(); } return csvData; }} The DBUtils class is a straightforward utility class that returns CSV content as a List of Strings. It also copies the original CSV file to the runtime directory of any application server we might be running. This may not be the best practice, but it makes it easier to manipulate the CSV file, and we always have the original CSV file untouched if and when we need to go back to the original version. The code for CountryDB is given here: package samples;import java.io.FileWriter;import java.io.IOException;import java.io.PrintWriter;import java.util.Arrays;import java.util.List;import java.util.Vector;public class CountryDB { private DBUtils dbUtils = new DBUtils(); private String fileName = "countries.csv"; public CountryDB() { dbUtils.initFileDB(fileName); } public String[] getCountryData(String ccode) { List<String> countries = dbUtils.getCSVStrings(null); for (String country : countries) { if (country.indexOf("," + ccode + ",") > -1) { return country.split(","); } } return new String[0]; } public List<List<String>> getCountries(String startLetter) { List<List<String>> allCountryData = new Vector<List<String>>(); List<String> countryData = dbUtils.getCSVStrings(startLetter); for (String country : countryData) { String[] data = country.split(","); allCountryData.add(Arrays.asList(data)); } return allCountryData; } public String[] saveCountryNotes(String ccode, String notes) { List<String> countries = dbUtils.getCSVStrings(null); try { PrintWriter pw = new PrintWriter(new FileWriter(fileName)); for (String country : countries) { if (country.indexOf("," + ccode + ",") > -1) { if (country.split(",").length == 4) { // no existing notes country = country + "," + notes; } else { if (notes.length() == 0) { country = country.substring(0, country .lastIndexOf(",")); } else { country = country.substring(0, country .lastIndexOf(",")) + "," + notes; } } } pw.println(country); } pw.close(); } catch (IOException ioe) { ioe.printStackTrace(); } String[] rv = new String[2]; rv[0] = ccode; rv[1] = notes; return rv; }} The CountryDB class is a remoted class. The getCountryData() method returns country data as an array of strings based on the country code. The getCountries() method returns all the countries that start with the specified parameter, and saveCountryNotes() saves user added notes to the country specified by the country code. In order to use CountryDB, the following script element must be added to the index.jsp file together with other JavaScript elements. <script type='text/javascript' src='/DWREasyAjax/dwr/interface/CountryDB.js'></script> There is one other Java class that we need to create and remote. That is the AppContent class that was already present in the JavaScript functions of the home page. The AppContent class is responsible for reading the content of the HTML file and parses the possible JavaScript function out of it, so it can become usable by the existing JavaScript functions in index.jsp file. package samples;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.InputStream;import java.util.List;import java.util.Vector;public class AppContent { public AppContent() { } public List<String> getContent(String contentId) { InputStream is = this.getClass().getClassLoader().getResourceAsStream( "samples/"+contentId+".html"); String content=streamToString(is); List<String> contentList=new Vector<String>(); //Javascript within script tag will be extracted and sent separately to client for(String script=getScript(content);!script.equals("");script=getScript(content)) { contentList.add(script); content=removeScript(content); } //content list will have all the javascript //functions, last element is executed last //and all other before html content if(contentList.size()>1) { contentList.add(contentList.size()-1, content); } else { contentList.add(content); } return contentList; } public List<String> getLetters() { List<String> letters=new Vector<String>(); char[] l=new char[1]; for(int i=65;i<91;i++) { l[0]=(char)i; letters.add(new String(l)); } return letters; } public String removeScript(String html) { //removes first script element int sIndex=html.toLowerCase().indexOf("<script "); if(sIndex==-1) { return html; } int eIndex=html.toLowerCase().indexOf("</script>")+9; return html.substring(0, sIndex)+html.substring(eIndex); } public String getScript(String html) { //returns first script element int sIndex=html.toLowerCase().indexOf("<script "); if(sIndex==-1) { return ""; } int eIndex=html.toLowerCase().indexOf("</script>")+9; return html.substring(sIndex, eIndex); } public String streamToString(InputStream is) { String content=""; try { ByteArrayOutputStream baos=new ByteArrayOutputStream(); for(int b=is.read();b!=-1;b=is.read()) { baos.write(b); } content=baos.toString(); } catch(IOException ioe) { content=ioe.toString(); } return content; }} The getContent() method reads the HTML code from a file based on the contentId. ContentId was specified in the dwrapplication.properties file, and the HTML is just contentId plus the extension .html in the package directory. There is also a getLetters() method that simply lists letters from A to Z and returns a list of letters to the browser. If we test the application now, we will get an error as shown in the following screenshot: We know why the AppContent is not defined error occurs, so let's fix it by adding AppContent to the allow element in the dwr.xml file. We also add CountryDB to the allow element. The first thing we do is to add required elements to the dwr.xml file. We add the following creators within the allow element in the dwr.xml file. <create creator="new" javascript="AppContent"> <param name="class" value="samples.AppContent" /> <include method="getContent" /> <include method="getLetters" /> </create> <create creator="new" javascript="CountryDB"> <param name="class" value="samples.CountryDB" /> <include method="getCountries" /> <include method="saveCountryNotes" /> <include method="getCountryData" /></create> We explicitly define the methods we are remoting using the include elements. This is a good practice, as we don't accidentally allow access to any methods that are not meant to be remoted. Client Code for Tables and Lists We also need to add a JavaScript interface to the index.jsp page. Add the following with the rest of the scripts in the index.jsp file. <script type='text/javascript' src='/DWREasyAjax/dwr/interface/AppContent.js'></script> Before testing, we need the sample HTML for the content area. The following HTML is in the TablesAndLists.html file under the samples directory: <h3>Countries</h3><p>Show countries starting with <select id="letters" onchange="selectLetter(this);return false;"> </select><br/>Doubleclick "Notes"-cell to add notes to country.</p><table border="1"> <thead> <tr> <th>Name</th> <th>Long name</th> <th>Code</th> <th>Capital</th> <th>Notes</th> </tr> </thead> <tbody id="countryData"> </tbody></table><script type='text/javascript'>//TO BE EVALEDAppContent.getLetters(addLetters);</script> The script element at the end is extracted by our Java class, and it is then evaluated by the browser when the client-side JavaScript receives the HTML. There is the select element, and its onchange event calls the selectLetter() JavaScript function. We will implement the selectLetter() function shortly. JavaScript functions are added in the index.jsp file, and within the head element. Functions could be in separate JavaScript files, but the embedded script is just fine here. function selectLetter(selectElement){ var selectedIndex = selectElement.selectedIndex; var selectedLetter= selectElement.options[selectedIndex ].value; CountryDB.getCountries(selectedLetter,setCountryRows);}function addLetters(letters){dwr.util.addOptions('letters',['letter...']);dwr.util.addOptions('letters',letters);}function setCountryRows(countryData){var cellFuncs = [ function(data) { return data[0]; }, function(data) { return data[1]; }, function(data) { return data[2]; }, function(data) { return data[3]; }, function(data) { return data[4]; }];dwr.util.removeAllRows('countryData');dwr.util.addRows( 'countryData',countryData,cellFuncs, { cellCreator:function(options) { var td = document.createElement("td"); if(options.cellNum==4) { var notes=options.rowData[4]; if(notes==undefined) { notes='&nbsp;';// + options.rowData[2]+'notes'; } var ccode=options.rowData[2]; var divId=ccode+'_Notes'; var tdId=divId+'Cell'; td.setAttribute('id',tdId); var html=getNotesHtml(ccode,notes); td.innerHTML=html; options.data=html; } return td; }, escapeHtml:false });}function getNotesHtml(ccode,notes){ var divId=ccode+'_Notes'; return "<div onDblClick="editCountryNotes('"+divId+"','"+ccode+"');" id=""+divId+"">"+notes+"</div>";}function editCountryNotes(id,ccode){ var notesElement=dwr.util.byId(id); var tdId=id+'Cell'; var notes=notesElement.innerHTML; if(notes=='&nbsp;') { notes=''; } var editBox='<input id="'+ccode+'NotesEditBox" type="text" value="'+notes+'"/><br/>'; editBox+="<input type='button' id='"+ccode+"SaveNotesButton' value='Save' onclick='saveCountryNotes(""+ccode+"");'/>"; editBox+="<input type='button' id='"+ccode+"CancelNotesButton' value='Cancel' onclick='cancelEditNotes(""+ccode+"");'/>"; tdElement=dwr.util.byId(tdId); tdElement.innerHTML=editBox; dwr.util.byId(ccode+'NotesEditBox').focus();}function cancelEditNotes(ccode){ var countryData=CountryDB.getCountryData(ccode, { callback:function(data) { var notes=data[4]; if(notes==undefined) { notes='&nbsp;'; } var html=getNotesHtml(ccode,notes); var tdId=ccode+'_NotesCell'; var td=dwr.util.byId(tdId); td.innerHTML=html; } });}function saveCountryNotes(ccode){ var editBox=dwr.util.byId(ccode+'NotesEditBox'); var newNotes=editBox.value; CountryDB.saveCountryNotes(ccode,newNotes, { callback:function(newNotes) { var ccode=newNotes[0]; var notes=newNotes[1]; var notesHtml=getNotesHtml(ccode,notes); var td=dwr.util.byId(ccode+"_NotesCell"); td.innerHTML=notesHtml; } });} There are lots of functions for table samples, and we go through each one of them. The first is the selectLetter() function. This function gets the selected letter from the select element and calls the CountryDB.getCountries() remoted Java method. The callback function is setCountryRows. This function receives the return value from the Java getCountries() method, that is List<List<String>>, a List of Lists of Strings. The second function is addLetters(letters), and it is a callback function for theAppContent.getLetters() method, which simply returns letters from A to Z. The addLetters() function uses the DWR utility functions to populate the letter list. Then there is a callback function for the CountryDB.getCountries() method. The parameter for the function is an array of countries that begin with a specified letter. Each array element has a format: Name, Long name, (country code) Code, Capital, Notes. The purpose of this function is to populate the table with country data; and let's see how it is done. The variable, cellFuncs, holds functions for retrieving data for each cell in a column. The parameter named data is an array of country data that was returned from the Java class. The table is populated using the DWR utility function, addRows(). The cellFuncs variable is used to get the correct data for the table cell. The cellCreator function is used to create custom HTML for the table cell. Default implementation generates just a td element, but our custom implementation generates the td-element with the div placeholder for user notes. The getNotesHtml() function is used to generate the div element with the event listener for double-click. The editCountryNotes() function is called when the table cell is double-clicked. The function creates input fields for editing notes with the Save and Cancel buttons. The cancelEditNotes() and saveCountryNotes() functions cancel the editing of new notes, or saves them by calling the CountryDB.saveCountryNotes() Java method. The following screenshot shows what the sample looks like with the populated table: Now that we have added necessary functions to the web page we can test the application. Testing Tables and Lists The application should be ready for testing if we have had the test environment running during development. Eclipse automatically deploys our new code to the server whenever something changes. So we can go right away to the test page http://127.0.0.1:8080/DWREasyAjax. On clicking Tables and lists we can see the page we have developed. By selecting some letter, for example "I" we get a list of all the countries that start with letter "I" (as shown in the previous screenshot). Now we can add notes to countries. We can double-click any table cell under Notes. For example, if we want to enter notes to Iceland, we double-click the Notes cell in Iceland's table row, and we get the edit box for the notes as shown in the following screenshot: The edit box is a simple text input field. We didn't use any forms. Saving and canceling editing is done using JavaScript and DWR. If we press Cancel, we get the original notes from the CountryDB Java class using DWR and saving also uses DWR to save data. CountryDB.saveCountryNotes() takes the country code and the notes that the user entered in the edit box and saves them to the CSV file. When notes are available, the application will show them in the country table together with other country information as shown in the following screenshot: Afterword The sample in this section uses DWR features to get data for the table and list from the server. We developed the application so that most of the application logic is written in JavaScript and Java beans that are remoted. In principle, the application logic can be thought of as being fully browser based, with some extensions in the server. Implementing Field Completion Nowadays, field completion is typical of many web pages. A typical use case is getting a stock quote, and field completion shows matching symbols as users type letters. Many Internet sites use this feature. Our sample here is a simple license text finder. We enter the license name in the input text field, and we use DWR to show the license names that start with the typed text. A list of possible completions is shown below the input field. The following is a screenshot of the field completion in action: Selected license content is shown in an iframe element from http://www.opensource.org. Server Code for Field Completion We will re-use some of the classes we developed in the last section. AppContent is used to load the sample page, and the DBUtils class is used in the LicenseDB class. The LicenseDB class is shown here: package samples;import java.util.List;import java.util.Vector;public class LicenseDB{ private DBUtils dbUtils=new DBUtils(); public LicenseDB() { dbUtils.initFileDB("licenses.csv"); } public List<String> getLicensesStartingWith(String startLetters) { List<String> list=new Vector<String>(); List<String> licenses=dbUtils.getCSVStrings(startLetters); for(String license : licenses) { list.add(license.split(",")[0]); } return list; } public String getLicenseContentUrl(String licenseName) { List<String> licenses=dbUtils.getCSVStrings(licenseName); if(licenses.size()>0) { return licenses.get(0).split(",")[1]; } return ""; }} The getLicenseStartingWith() method goes through the license names and returns valid license names and their URLs. Similar to the data in the previous section, license data is in a CSV file named licenses.csv in the package directory. The following is an excerpt of the file content: Academic Free License, http://opensource.org/licenses/afl-3.0.phpAdaptive Public License, http://opensource.org/licenses/apl1.0.phpApache Software License, http://opensource.org/licenses/apachepl-1.1.phpApache License, http://opensource.org/licenses/apache2.0.phpApple Public Source License, http://opensource.org/licenses/apsl-2.0.phpArtistic license, http://opensource.org/licenses/artistic-license-1.0.php... There are quite a few open-source licenses. Some are more popular than others (like the Apache Software License) and some cannot be re-used (like the IBM Public License). We want to remote the LicenseDB class, so we add the following to the dwr.xml file. <create creator="new" javascript="LicenseDB"> <param name="class" value="samples.LicenseDB"/> <include method="getLicensesStartingWith"/> <include method="getLicenseContentUrl"/></create> Client Code for Field Completion The following script element will go in the index.jsp page. <script type='text/javascript' src='/DWREasyAjax/dwr/interface/LicenseDB.js'></script> The HTML for the field completion is as follows: <h3>Field completion</h3><p>Enter Open Source license name to see it's contents.</p><input type="text" id="licenseNameEditBox" value="" onkeyup="showPopupMenu()" size="40"/><input type="button" id="showLicenseTextButton" value="Show license text" onclick="showLicenseText()"/><div id="completionMenuPopup"></div><div id="licenseContent"></div> The input element, where we enter the license name, listens to the onkeyup event which calls the showPopupMenu() JavaScript function. Clicking the Input button calls the showLicenseText() function (the JavaScript functions are explained shortly). Finally, the two div elements are place holders for the pop-up menu and the iframe element that shows license content. For the pop-up box functionality, we use existing code and modify it for our purpose (many thanks to http://www.jtricks.com). The following is the popup.js file, which is located under the WebContent | js directory. //<script type="text/javascript"><!--/* Original script by: www.jtricks.com * Version: 20070301 * Latest version: * www.jtricks.com/javascript/window/box.html * * Modified by Sami Salkosuo. */// Moves the box object to be directly beneath an object.function move_box(an, box){ var cleft = 0; var ctop = 0; var obj = an; while (obj.offsetParent) { cleft += obj.offsetLeft; ctop += obj.offsetTop; obj = obj.offsetParent; } box.style.left = cleft + 'px'; ctop += an.offsetHeight + 8; // Handle Internet Explorer body margins, // which affect normal document, but not // absolute-positioned stuff. if (document.body.currentStyle && document.body.currentStyle['marginTop']) { ctop += parseInt( document.body.currentStyle['marginTop']); } box.style.top = ctop + 'px';}var popupMenuInitialised=false;// Shows a box if it wasn't shown yet or is hidden// or hides it if it is currently shownfunction show_box(html, width, height, borderStyle,id){ // Create box object through DOM var boxdiv = document.getElementById(id); boxdiv.style.display='block'; if(popupMenuInitialised==false) { //boxdiv = document.createElement('div'); boxdiv.setAttribute('id', id); boxdiv.style.display = 'block'; boxdiv.style.position = 'absolute'; boxdiv.style.width = width + 'px'; boxdiv.style.height = height + 'px'; boxdiv.style.border = borderStyle; boxdiv.style.textAlign = 'right'; boxdiv.style.padding = '4px'; boxdiv.style.background = '#FFFFFF'; boxdiv.style.zIndex='99'; popupMenuInitialised=true; //document.body.appendChild(boxdiv); } var contentId=id+'Content'; var contents = document.getElementById(contentId); if(contents==null) { contents = document.createElement('div'); contents.setAttribute('id', id+'Content'); contents.style.textAlign= 'left'; boxdiv.contents = contents; boxdiv.appendChild(contents); } move_box(html, boxdiv); contents.innerHTML= html; return false;}function hide_box(id){ document.getElementById(id).style.display='none'; var boxdiv = document.getElementById(id+'Content'); if(boxdiv!=null) { boxdiv.parentNode.removeChild(boxdiv); } return false;}//--></script> Functions in the popup.js file are used as menu options directly below the edit box. The show_box() function takes the following arguments: HTML code for the pop-up, position of the pop-up window, and the "parent" element (to which the pop-up box is related). The function then creates a pop-up window using DOM. The move_box() function is used to move the pop-up window to its correct place under the edit box and the hide_box() function hides the pop-up window by removing the pop-up window from the DOM tree. In order to use functions in popup.js, we need to add the following script-element to the index.jsp file: <script type='text/javascript' src='js/popup.js'></script> Our own JavaScript code for the field completion is in the index.jsp file. The following are the JavaScript functions, and an explanation follows the code: function showPopupMenu(){ var licenseNameEditBox=dwr.util.byId('licenseNameEditBox'); var startLetters=licenseNameEditBox.value; LicenseDB.getLicensesStartingWith(startLetters, { callback:function(licenses) { var html=""; if(licenses.length==0) { return; } if(licenses.length==1) { hidePopupMenu(); licenseNameEditBox.value=licenses[0]; } else { for (index in licenses) { var licenseName=licenses[index];//.split(",")[0]; licenseName=licenseName.replace(/"/g,"&quot;"); html+="<div style="border:1px solid #777777;margin-bottom:5;" onclick="completeEditBox('"+licenseName+"');">"+licenseName+"</div>"; } show_box(html, 200, 270, '1px solid','completionMenuPopup'); } } });}function hidePopupMenu(){ hide_box('completionMenuPopup');}function completeEditBox(licenseName){ var licenseNameEditBox=dwr.util.byId('licenseNameEditBox'); licenseNameEditBox.value=licenseName; hidePopupMenu(); dwr.util.byId('showLicenseTextButton').focus();}function showLicenseText(){ var licenseNameEditBox=dwr.util.byId('licenseNameEditBox'); licenseName=licenseNameEditBox.value; LicenseDB.getLicenseContentUrl(licenseName,{ callback:function(licenseUrl) { var html='<iframe src="'+licenseUrl+'" width="100%" height="600"></iframe>'; var content=dwr.util.byId('licenseContent'); content.style.zIndex="1"; content.innerHTML=html; } });} The showPopupMenu() function is called each time a user enters a letter in the input box. The function gets the value of the input field and calls the LicenseDB. getLicensesStartingWith() method. The callback function is specified in the function parameters. The callback function gets all the licenses that match the parameter, and based on the length of the parameter (which is an array), it either shows a pop-up box with all the matching license names, or, if the array length is one, hides the pop-up box and inserts the full license name in the text field. In the pop up box, the license names are wrapped within the div element that has an onclick event listener that calls the completeEditBox() function. The hidePopupMenu() function just closes the pop-up menu and the competeEditBox() function inserts the clicked license text in the input box and moves the focus to the button. The showLicenseText() function is called when we click the Show license text button. The function calls the LicenseDB. getLicenseContentUrl() method and the callback function creates an iframe element to show the license content directly from http://www.opensource.org, as shown in the following screenshot: Afterword Field completion improves user experience in web pages and the sample code in this section showed one way of doing it using DWR. It should be noted that the sample for field completion presented here is only for demonstration purposes.
Read more
  • 0
  • 0
  • 3104

article-image-er-diagrams-domain-model-and-n-layer-architecture-aspnet-35-part2
Packt
20 Oct 2009
5 min read
Save for later

ER Diagrams, Domain Model, and N-Layer Architecture with ASP.NET 3.5 (part2)

Packt
20 Oct 2009
5 min read
1-tier 3-layer Architecture using a Domain Model Based on the class diagram in the first part, we will create a new simple 3-layered application using the entities defined in the above domain model. We will create a new ASP.NET Web Project in VS. This time, you should create two new folders inside your root web folder (using the Add New Folder option in VS): BL: This folder will contain all of the business logic domain classes DAL: This folder will contain the data access code files (for each entity) Layer 1: Data Access Layer (DAL) First, we will create a DAL class for each entity. We will name each DAL class using this naming pattern: EntityDAL. Let us see the CustomerDAL class: using DomainModel.BL;namespace DomainModel.DAL{ public class CustomerDAL { public static void AddCustomer(Customer cs) { using (SqlConnection con = new SqlConnection(SQLHelper.GetConnectionString())) { SqlParameter[] par = new SqlParameter[4]; par[0] = new SqlParameter("@customerID", cs.ID); par[0].Direction = ParameterDirection.Output; par[1] = new SqlParameter("@name", cs.Name); par[2] = new SqlParameter("@address", cs.Address); par[3] = new SqlParameter( "@phoneNo", cs.PhoneNumber); int rowNo = SQLHelper.ExecuteNonQuery( con, CommandType.StoredProcedure, "OMS_AddCustomer", par); cs.ID = Convert.ToInt32(par[0].Value); } } public static void DeleteCustomer(int customerID) { using (SqlConnection con = new SqlConnection(SQLHelper.GetConnectionString())) { SqlParameter[] par = new SqlParameter[1]; par[0] = new SqlParameter("@customerID", customerID); int rowNo = SQLHelper.ExecuteNonQuery( con, CommandType.StoredProcedure, "OMS_DeleteCustomer", par); } } public static void UpdateCustomer(Customer cs) { using (SqlConnection con = new SqlConnection(SQLHelper.GetConnectionString())) { SqlParameter[] par = new SqlParameter[4]; par[0] = new SqlParameter("@customerID", cs.ID); par[1] = new SqlParameter("@address", cs.Address); par[2] = new SqlParameter("@name", cs.Name); par[3] = new SqlParameter( "@phoneNo", cs.PhoneNumber); int rowNo = SQLHelper.ExecuteNonQuery( con, CommandType.StoredProcedure, "OMS_UpdateCustomer", par); } } public static void GetCustomer(Customer cs) { using (SqlConnection con = new SqlConnection(SQLHelper.GetConnectionString())) { SqlParameter[] par = new SqlParameter[1]; par[0] = new SqlParameter("@customerID", customerID); using (SqlDataReader dr = SQLHelper.ExecuteReader(con, CommandType.StoredProcedure, "OMS_GetCustomer", par)) { c = new Customer(); while (dr.Read()) { c.Name = SQLHelper.CheckStringNull(dr["Name"]); c.PhoneNumber = SQLHelper.CheckStringNull(dr["PhoneNo"]); c.Address = SQLHelper.CheckStringNull(dr["Address"]); c.ID = SQLHelper.CheckIntNull(dr["ID"]); } } } } public static List<Customer> GetAllCustomers() { List<Customer> cuList = new List<Customer>(); using (SqlConnection con = new SqlConnection(SQLHelper.GetConnectionString())) { using (SqlDataReader dr = SQLHelper.ExecuteReader(con,CommandType. StoredProcedure,"OMS_GetAllCustomer")) { while (dr.Read()) { Customer customer = new Customer(); customer.Name = SQLHelper.CheckStringNull(dr["Name"]); customer.PhoneNumber = SQLHelper.CheckStringNull(dr["PhoneNo"]); customer.Address = SQLHelper.CheckStringNull(dr["Address"]); customer.ID = SQLHelper.CheckIntNull(dr["ID"]); cuList.Add(customer); } } } return cuList; } }//end class} Here, we have used the SqlHelper class, which contains generic data access utility methods, so that we can avoid code repletion. Layer 2: Business Layer (BL) Next, we will create classes for each of the domain entities. We will put all of these classes under the new BL folder with this namespace: DomainModel.BL. Create a new C# class file named Customer.cs under the BL folder. Here is the first Customer class: using DomainModel.DAL;namespace DomainModel.BL{ public class Customer { private int _ID; private string _name; private string _address; private string _phoneNumber; private List<Customer> _customerCollection; public int ID { get { return _ID; } set { _ID = value; } } public string Name { get { return _name; } set { _name = value; } } public string Address { get { return _address; } set { _address = value; } } public string PhoneNumber { get { return _phoneNumber; } set { _phoneNumber = value; } } public List<Customer> CustomerCollection { get { return _customerCollection; } set { _customerCollection = value; } } public void Add() { CustomerDAL.AddCustomer(this); } public void Delete(int customerID) { CustomerDAL.DeleteCustomer(this.ID); } public void Update() { CustomerDAL.UpdateCustomer(this); } public void Load() { CustomerDAL.GetCustomer(this.ID); } public void GetAll() { this.CustomerCollection = CustomerDAL.GetAllCustomers(); } }//end class}//end namespace The CustomerDAL class is pretty simple: we are fetching the data from the database using data readers, and performing all data related operations using the Customer business object. This Customer class is defined in the Customer.cs class we created earlier. This BL class is calling DAL methods, so it needs a reference to the DAL namespace (using DomainModel.DAL). Similarly, the DAL class we created earlier used Customer business objects. That's why it also needed the BL namespace. We are using generics to create a collection of Customer objects. The BL communicates with DAL to get the data and perform updates. Now, we will see how the UI (which is under a different namespace) talks to BL without even knowing about DAL. Layer 3: The UI Layer Here is the code in the AllCustomers.aspx.cs page that shows a list of all of the customers from the DB (there is a data list on the web form, which will show a list of the customers): using DomainModel.BL;namespace DomainModel.UI { //page load private void FillAllCustomers() { Customer c = new Customer(); c.GetAll(); List<Customer> cuList = c.CustomerCollection; dtlstAllCustomer.DataSource = cuList; dtlstAllCustomer.DataBind(); } } So in the UI class, we neither have any data access code nor are we calling data access class methods from this layer (as was the case with the 1-tier 2-layer style we saw earlier in this article). We have a reference to the BL layer (using DomainModel.BL), and we are using the Customer business object to return a generic list of customer objects, which we are binding to the data list control (showing a list of all the customers). So the GUI layer does not know anything about the DAL layer, and is completely independent of it. The idea here is to understand how a 3-Layer architecture can provide more flexibility and loose-coupling to your project. In the next section, we will learn how we can use object data source controls to implement a 3-layer architecture without writing much code ourselves.
Read more
  • 0
  • 0
  • 2295
article-image-implementing-workflow-alfresco-3
Packt
20 Oct 2009
5 min read
Save for later

Implementing Workflow in Alfresco 3

Packt
20 Oct 2009
5 min read
Workflow is the automation of a business process, during which documents are passed from one participant to another for action, according to a set of procedural rules. Every Content Management System implementation has its own workflow requirements. For some companies, workflow could be a simple approval process. For some companies, it could be a complex business process management system. Workflow provides ownership and control over the content and processes. Introduction to the Alfresco workflow process Alfresco includes two types of out of the box workflow. The first is the Simple Workflow, which is content-oriented, and the other is the Advanced Workflow, which is task-oriented. The Simple Workflow process in Alfresco involves the movement of documents through various spaces.A content item is moved or copied to a new space at which point a new workflow instance is attached, which is based on the workflow definition of the space. A workflow definition is unaware of other related workflow definitions. The Advanced Workflow process is task-oriented, where you create a task, attach documents that are to be reviewed, and assign it to appropriate reviewers. The same robust workflow capabilities are available in Document Management (DM), Records Management (RM), Web Content Management (WCM), and throughout our applications, which includes Alfresco Share. You can use the out of the box features provided by both types of workflow, or you can create your own custom advanced workflow, according to the business processes of your organization. Simple Workflow Consider a purchase order that moves through various departments for authorization and eventual purchase. To implement Simple Workflow for this in Alfresco, you will create spaces for each department and allow documents to move through various department spaces. Each department space is secured, only allowing the users of that department to edit the document and to move it to the next departmental space in the workflow process. The workflow process is so flexible that you could introduce new steps for approval into the operation without changing any code. Out of the box features Simple Workflow is implemented as an aspect that can be attached to any document in a space through the use of business rules. Workflow can also be invoked on individual content items as actions. Workflow has two steps. One is for approval while the other one is for rejection. You can refer to the upcoming image, where workflow is defined for the documents in a space called Review Space. The users belonging to the Review Space can act upon the document. If they choose to Reject, then the document moves to a space called Rejected Space. If they choose to Approve, then the document moves to a space called Approved Space. You can define the names of the spaces and the users on the spaces, according to your business requirements. The following figure gives a graphical view of the Approved Space and the Rejected Space: Define and use Simple Workflow The process to define and use Simple Workflow in Alfresco is as follows: Identify spaces and set security on those spaces Define your workflow process Add workflow to content in those spaces, accordingly Select the email template and the people to send email notifications to Test the workflow process Let us define and use a Simple Workflow process to review and approve the engineering documents on your intranet. Go to the Company Home > Intranet > Engineering Department space and create a space named ProjectA by using an existing Software Engineering Project space template. Identify spaces and security If you go to the Company Home > Intranet > Engineering Department > ProjectA > Documentation space, then you will notice the following sub-spaces: Samples: This space is for storing sample project documents. Set the security on this space in such a way that only the managers can edit the documents. Drafts: This space contains initial drafts and documents of ProjectA that are being edited. Set the security in such a way that only a few selected users (such as Engineer1, Engineer2— as shown in the upcoming image) can add or edit the documents in this space. Pending Approval: This space contains all of the documents that are under review. Set the security in such a way that only the Project Manager of ProjectA can edit these documents. Published: This space contains all of the documents that are Approved and visible to others. Nobody should edit the documents while they are in the Published space. If you need to edit a document, then you need to Retract it to the Drafts space and follow the workflow process, as shown in the following image:    Defining the workflow process Now that you have identified the spaces, the next step is to define your workflow process. We will add workflow to all of the documents in the Drafts space. When a user selects the Approve action called Submit for Approval on a document, then the document moves from the Drafts space to the Pending Approval space. We will add workflow to all of the documents in the Pending Approval space. When a user selects the Approve action called Approved on a document, then the document moves from the Pending Approval space to the Published space. Similarly, when a user selects the Reject action called Re-submit on a document, then it moves from the Pending Approval space to the Drafts space. We will add workflow to all of the documents in the Published space. When a user selects the Reject action called Retract on a document, then it moves from the Published space to the Drafts space. You can have review steps and workflow action names according to your business's requirements.
Read more
  • 0
  • 0
  • 2474

article-image-date-and-calendar-module-drupal-5-part-1
Packt
20 Oct 2009
5 min read
Save for later

Date and Calendar Module in Drupal 5: Part 1

Packt
20 Oct 2009
5 min read
Recipe 33: Understanding Date formats Drupal dates are typically stored in one of two ways. Core Drupal dates—including Node: Created Time, and Node: Updated Time—are stored as Unix timestamps. Contributed module date fields can be stored as either a timestamp or a format known as ISO. Neither style is particularly friendly to human readers, so both field types are usually formatted before users see them. This recipe offers a tour of places in Drupal where dates can be formatted and information on how to customize the formats. What's that Lucky Day?The Unix timestamp 1234567890 fell on Friday the 13th, in February, 2009. This timestamp marks 1,234,567,890 seconds since January 1, 1970. The same date/time combination would be stored in a date field in ISO format as 2009-02-13T23:31:30+00:0. ISO is an abbreviation for the International Organization for Standardization Opening the browser windows side-by-side will help you understand date formatting. In the left window, open YOURSITE.com/admin/settings/ date-time to view the settings page for date and time. In the right window, open the API page of code that defines these system date time settings at http://api.drupal.org/api/function/system_date_time_settings/5. Compare each item in the $datemedium array, for instance, with the associated Medium date format drop-down. a – am/pm D – Day, Mon through Sun d – Date, 01 to 31 (with leading zeroes) F – Month, January through December (mnemonic, F = Full name) g – Hours, 1 through 12 H – Hours, 00 through 23 i – Minutes, 00 to 59 j – Date, 1 to 31 (No leading zeroes) l – Sunday through Saturday m – Month, 01 through 12 M – Month, Jan through Dec s – Seconds, 00 through 59 (with leading zeroes) S – Month Suffix, st, nd, rd, or th. Works well with j Y – Year, Examples: 1999 or 2011 Below is the list of codes for many commonly used date and time formats. A more comprehensive list appears at http://us.php.net/date. Explore Drupal places where these codes may be used. The first four locations in the table below are available in the Drupal administrative interface. The last three involve editing files on the server—these edits are completely optional. Location Details CCK field setup Custom Input formats admin/content/types/story/add_field   After the field widget is specified admin/content/types/<CONTENTTYPE>/fields/field_<FIELDNAME> Near the top of the page.   Near the bottom of the page:   Formatting Fields in Views. admin/build/views/<VIEW_NAME>/edit CCK Date fields are set via the Options drop-down in the Fields fieldset.   Custom date formats for core fields, such as Node: Created Time are set via handler and options from elements.   Default Date and Time settings admin/settings/date-time Set the default time zone, Short, Medium, and Long date formats, and the first day of the week.   Post Settings This may be one of the harder-to-find settings in Drupal, enabling the Post settings to be turned-off for specified content types. (An example of a post setting would be: Submitted by admin on Sun, 10/12/2008 - 4:55pm. The setting is found on the right-hand side of this URL: admin/build/themes/settings Use the following mouse click trail to get to this URL: Administer | Site Building | Themes | Configure (Click on the Configure tab at the top of the page. If you click on the Configure link in the Operations column, you will still need to click the Configure tab at the top to get to the global settings.)   Variable overrides in settings.php You may override variables at the bottom of the /sites/default/settings.php file. Remove the appropriate pound signs to enable the $conf array, and add a setting as shown below. Note that this is a quick way to modify the post settings format, which draws from the medium date variable. $conf = array( #   'site_name' => 'My Drupal site', #   'theme_default' => 'minnelli', #   'anonymous' => 'Visitor', 'date_format_medium' => 'l F d, Y'  ); *.tpl.php files Examples: node-story.tpl.php <?php print format_date($node->created, 'custom', 'F Y'); ?> comment.tpl.php <?php echo t('On ') . format_date($comment->timestamp,   'custom'  , 'F jS, Y'); ?> <?php echo theme('username',   $comment) . t(' says:'); ?> template.php Redefine $variables['submitted'] Example from blommor01 theme:   $vars['submitted'] =  t('!user - <abbr class="created"   title="!microdate">!date</abbr>', array(    '!user' => theme('username', $vars['node']),    '!date' => format_date($vars['node']->created),    '!microdate' => format_date($vars['node']->   created,'custom', "Y-m-dTH:i:sO")   )); Recipe notes Note that when using the PHP date codes, additional characters may be added, including commas, spaces, and letters. In the template.php example, a backslash was used to show that the letter 'T' will be printed, rather than the formatted return values. Below are more examples of added characters: F j, Y, g:i a // August 27, 2010, 5:16 pmm.d.y // 08.27.10 You may occasionally find that an online date converter comes in handy. http://www.timestampconverterer.com/ (this URL includes the word "converter" followed by another "er"). http://www.coryking.com/date-converter.php
Read more
  • 0
  • 0
  • 2719

article-image-mastering-phpmyadmin-4-editions-5-years
Packt
20 Oct 2009
4 min read
Save for later

Mastering phpMyAdmin: 4 Editions in 5 years

Packt
20 Oct 2009
4 min read
Among the daily deluge of spam, sometimes there is a real email – and sometimes even an important one. I received such an email at the end of October 2003 from Louay Fatoohi, presenting himself as the Editorial Director of a new publishing company, Packt Publishing. He was asking if I would be interested in writing a book about phpMyAdmin (which is a web interface for the MySQL database). At this time, our software product was already popular with about 150,000 downloads per month, but the lack of serious and complete user documentation held it back. This translated into many requests from users about how to accomplish some tasks with the software. Moreover, the small team of five active developers (all volunteers) was using all its free time for planning and coding new features so we did not see how to improve the documentation with volunteer work only. I did not know Mr Fatoohi but his email (and the following ones) looked serious so I committed myself to this project. I noticed that producing a clear book outline was very important. I already had in mind that each chapter should focus on a specific task or group of similar tasks. In the next days, a few iterations of the outline were done to balance the chapter lengths. Receiving a paper contract from England was new for me – in fact the whole process felt new to me as this was my first book. Writing for others is challenging and dealing with the comments of reviewers and editors can be a humbling experience; I had to defend some ideas I had put in the book but gladly accepted suggestions for improvement or clarification. It took about three months, working over the weekends (I have a day job) to write the first edition, Mastering phpMyAdmin for Effective MySQL Management, which was published in April 2004. Since this manual covers the complete interface, I was in fact testing the whole software, finding bugs along the way, which forced me to fix them in order to produce correct screenshots! Needless to say, I was very proud of being the first published author for this company. Meanwhile, an interface redesign was taking place in phpMyAdmin for version 2.6.0; this meant that many screenshots in the book would no longer reflect the upcoming version of the software. The decision was made by Packt to publish a book update. During the summer of 2004, I already knew what the new version would look like so I started preparing this update. The new version of the software was released at the end of September and the book update (bearing the same ISBN as the first edition) somewhere in October! In June 2005 we organized the first phpMyAdmin team meeting in Germany during the LinuxTag event where we demoed our software. I was happy to meet Mr Fatoohi, Damian Carvill and other representatives from Packt who had a booth there too. I found the whole team very dynamic and very much readers-oriented. I know that publishing is a business but they understand that quality is important; they also respect me as an author (especially about schedules). Over the years, translations of the book were published, directly by Packt or via other publishers. I wrote the French version, while two phpMyAdmin developers did the German one. Versions in Czech, Italian and Spanish were also born and they are all proudly displayed in my house near the workstation. Meanwhile, phpMyAdmin’s march was continuing, with an average of 250,000 downloads per month. It should be noted that many users do not need to download it directly because host providers install the software for them, or Linux distributors include it on their DVD, this is why we cannot know exactly the number of users. The development team changed a bit but new features were appearing so I guess Packt Publishing resigned itself about the need to update this title from time to time. This is why I took to my pencil again – rather my keyboard – to produce the October 2006 edition, Mastering phpMyAdmin 2.8 which covered a new setup mechanism. In March 2008, Mastering phpMyAdmin 2.11 was published, followed by Mastering phpMyAdmin 3.1 in March 2009. My family is now accustomed to my new habit of taking a few weeks per year to produce the new update. I would like to express my wholeheartedly gratitude to Packt Publishing for their support into this ongoing project. --Marc Delisle
Read more
  • 0
  • 0
  • 4612
article-image-rotating-post-titles-post-preview-gadget
Packt
20 Oct 2009
6 min read
Save for later

The Rotating Post Titles with Post Preview Gadget

Packt
20 Oct 2009
6 min read
The Rotating Post Titles with Post Preview gadget lists all your blog posts classified according to labels or categories. Blogger uses Labels to classify posts while Wordpress uses Categories for the same. Clicking on a Label or a category in the sidebar of a blog brings up all posts associated with that particular label or category. However, you will see only posts associated with that one label. In this gadget post titles are grouped under their respective labels. In this gadget in one look you can see all the post titles in that blog and all the labels in it. Thus you get a full summary of the blog. Hovering on a post title shows the Post Preview in the top pane. You can then click on it to go to that post to read it in full detail. What is Google AJAX feed API? AJAX (shorthand for asynchronous JavaScript and XML) is a web development technique which retrieves data from the server asynchronously in the background without interfering with the display, and behaviour of the existing page. The whole page is not refreshed when data is retrieved. Only that section of the page which is a part of the gadget shows the data brought. With the Google AJAX Feed API, you can retrieve feeds and mash them up using Javascript. In this gadget, we will retrieve the post titles from the label feeds and display them using Javascript code. See picture below: This gadget shows list of posts grouped by label from my blog http://www.blogdoctor.me. Four post titles from three labels are shown but the code can be modified to show all posts from all labels (categories). This label is also shown as Gadget No 4 in My Gadget Showcase blog. The cursor autoscrolls down the post titles, and each post preview is shown at the top as an excerpt for five seconds before moving on to the next post. Obtaining the Google AJAX API Key The first step in installing the above gadget is to get the Google AJAX API Key. It is free and you can easily obtain it for any site blog or page by signing up for the key at the API key signup page. Type in your blog address in the My web site URL text box and click the "Generate API Key" button. On the resulting page copy the key and paste it in code below as shown. Customizing the code In the code below replace PASTE AJAX API KEY HERE with your actual key obtained above. <!-- ++Begin Dynamic Feed Wizard Generated Code++ --><!-- // Created with a Google AJAX Search and Feed Wizard // http://code.google.com/apis/ajaxsearch/wizards.html --> <!-- // The Following div element will end up holding the actual feed control. // You can place this anywhere on your page. --><div id="content"> <span style="color:#676767;font-size:11px;margin:10px;padding:4px;">Loading...</span> </div><!-- Google Ajax Api --> <script src="http://www.google.com/jsapi?key=PASTE AJAX API KEY HERE" type="text/javascript"></script> <!-- Dynamic Feed Control and Stylesheet --> <script src="http://www.google.com/uds/solutions/dynamicfeed/gfdynamicfeedcontrol.js" type="text/javascript"></script> <style type="text/css"> @import url("http://www.google.com/uds/solutions/dynamicfeed/gfdynamicfeedcontrol.css"); </style><script type="text/javascript"> google.load('feeds', '1'); function OnLoad() { var feeds = [ { title: 'LABEL_1', url: 'http://MYBLOG.blogspot.com/feeds/posts/default/-/LABEL1?max-results=100' }, { title: 'LABEL_2', url: 'http://MYBLOG.blogspot.com/feeds/posts/default/-/LABEL2?max-results=100' }, { title: 'LABEL_3', url: 'http://MYBLOG.blogspot.com/feeds/posts/default/-/LABEL3?max-results=100' } ]; var options = { stacked : true, horizontal : false, title : "Posts from BLOG_TITLE" }; new GFdynamicFeedControl(feeds, 'content', options); document.getElementById('content').style.width = "200px"; } google.setOnLoadCallback(OnLoad); </script> In the above code replace LABEL_1, LABEL_2 and LABEL_3 and LABEL1, LABEL2 and LABEL3 by respective Label Names and BLOG_TITLE by the actual title of your blog. Also replace MYBLOG by actual blog subdomain. This is for blogspot blogs only. For Wordpress blog you will have to replace the label feeds:: http://MYBLOG.blogspot.com/feeds/posts/default/-/LABEL1?max-results=100 http://MYBLOG.blogspot.com/feeds/posts/default/-/LABEL2?max-results=100 http://MYBLOG.blogspot.com/feeds/posts/default/-/LABEL3?max-results=100 by the Category feed URLs from Wordpress blog. After customizing the above code in Blogger paste it in a HTML gadget while in Wordpress paste it in a Text widget. Further Customization To show more than four posts per label or category, you will have to modify the following Javascript code file: http://www.google.com/uds/solutions/dynamicfeed/gfdynamicfeedcontrol.js In the above mentioned Javascript code file, alter the following code line in it : GFdynamicFeedControl.DEFAULT_NUM_RESULTS = 4; Change '4' to '1000' and save the file as a MODgfdynamicfeedcontrol.js file in a text editor like Notepad. Upload the file to a free host and replace the link of the file in the above code. To change the styling of the gadget, you will have to modify the following file: http://www.google.com/uds/solutions/dynamicfeed/gfdynamicfeedcontrol.css Then save the modified file and upload it to a free host and replace its link in the above code. Working Example Posts from The Blog Doctor Change Post Formatting According to Author. by Vin - 30 Apr 2008 If you have a Team Blog made up of two authors you can change the formatting of the posts written by one author to ... Template Calling all Newbie Bloggers! Upgrade Classic Template without the "UPGRADE YOUR TEMPLATE" button. The Minimalist Minima Photoblog Template. Making the COMMENTS Link more User Friendly. CSS Change Post Formatting According to Author. Free CSS Navigation Menus in Blogger. Fix the Page Elements Layout Editor No Scrollbar Problem. Frame the Blog Header Image. Blogger Hacks Rounded Corner Headers for Blogger. Timestamp under the Date and Other Hacks. Add Icon to Post Titles. Many Headers In One Blog.   Summary The Rotating Post Titles with Post Preview gadget provides an at-a glance summary of your blog. All the posts are grouped by label or category and are linked to their post pages. The constantly rotating post excerpts at the top draws the attention of the reader and gets him/her more involved and eager to explore your blog. This increases the traffic and decreases the bounce rate of visitors from your blog.nara
Read more
  • 0
  • 0
  • 3925

article-image-buttons-menus-and-toolbars-ext-js
Packt
20 Oct 2009
5 min read
Save for later

Buttons, Menus, and Toolbars in Ext JS

Packt
20 Oct 2009
5 min read
The unsung heroes of every application are the simple things like buttons, menus, and toolbars. In this article by Shea Frederick, Steve 'Cutter' Blades, and Colin Ramsay, we will cover how to add these items to our applications. Our example will contain a few different types of buttons, both with and without menus. A button can simply be an icon, or text, or both. Toolbars also have some mechanical elements such as spacers and dividers that can help to organize the buttons on your toolbars items. We will also cover how to make these elements react to user interaction. A toolbar for every occasion Just about every Ext component—panels, windows, grids can accept a toolbar on either the top or the bottom. The option is also available to render the toolbar standalone into any DOM element in our document. The toolbar is an extremely flexible and useful component that will no doubt be used in every application. Ext.Toolbar: The main container for the buttons Ext.Button: The primary handler for button creation and interaction Ext.menu: A menu Toolbars Our first toolbar is going to be rendered standalone in the body of our document. We will add one of each of the main button types, so we can experiment with each: Button—tbbutton: This is the standard button that we are all familiar with. Split Button—tbsplit: A split button is where you have a default button action and an optional menu. These are used in cases where you need to have many options in the same category as your button, of which there is a most commonly used default option. Menu—tbbutton+menu: A menu is just a button with the menu config filled in with options. Ext.onReady(function(){ new Ext.Toolbar({ renderTo: document.body, items: [{ xtype: 'tbbutton', text: 'Button' },{ xtype: 'tbbutton', text: 'Menu Button', menu: [{ text: 'Better' },{ text: 'Good' },{ text: 'Best' }] },{ xtype: 'tbsplit', text: 'Split Button', menu: [{ text: 'Item One' },{ text: 'Item Two' },{ text: 'Item Three' }] }] });}); As usual, everything is inside our onReady event handler. The items config holds all of our toolbars elements—I say elements and not buttons because the toolbar can accept many different types of Ext components including form fields—which we will be implementing later on in this article. The default xtype for each element in the items config is tbbutton. We can leave out the xtype config element if tbbutton is the type we want, but I like to include it just to help me keep track. The button Creating a button is fairly straightforward; the main config option is the text that is displayed on the button. We can also add an icon to be used alongside the text if we want to. Here is a stripped-down button: { xtype: 'tbbutton', text: 'Button'} Menu A menu is just a button with the menu config populated—it's that simple. The menu items work along the same principles as the buttons. They can have icons, classes, and handlers assigned to them. The menu items could also be grouped together to form a set of option buttons, but first let's create a standard menu. This is the config for a typical menu config: { xtype: 'tbbutton', text: 'Button', menu: [{ text: 'Better' },{ text: 'Good' },{ text: 'Best' }]} As we can see, once the menu array config is populated, the menu comes to life. To group these menu items together, we would need to set the group config and the boolean checked value for each item: menu: [{ text: 'Better', checked: true, group: 'quality'}, { text: 'Good', checked: false, group: 'quality'}, { text: 'Best', checked: false, group: 'quality'}] Split button The split button sounds like a complex component, but it's just like a button and a menu combined, with a slight twist. By using this type of button, you get to use the functionality of a button while adding the option to select an item from the attached menu. Clicking the left portion of the button that contains the text triggers the button action. However, clicking the right side of the button, which contains a small down arrow, triggers the menu. { xtype: 'tbsplit', text: 'Split Button', menu: [{ text: 'Item One' },{ text: 'Item Two' },{ text: 'Item Three' }]} Toolbar item alignment, dividers, and spacers By default, every toolbar aligns elements to the leftmost side. There is no alignment config for a toolbar, so if we want to align all of the toolbar buttons to the rightmost side, we need to add a fill as the first item in the toolbar. If we want to have items split up between both the left and right sides, we can also use a fill: { xtype: 'tbfill'} Pop this little guy in a tool-bar wherever you want to add space and he will push items on either side of the fill to the ends of the tool bar, as shown below: We also have elements that can add space or vertical dividers, like the one used between the Menu Button and the Split Button. The spacer adds a few pixels of empty space that can be used to space out buttons, or move elements away from the edge of the toolbar: { xtype: 'tbspacer'} A divider can be added in the same way: { xtype: 'tbseparator'} Shortcuts Ext has many shortcuts that can be used to make coding faster. Shortcuts are a character or two that can be used in place of a configuration object. For example, consider the standard toolbar filler configuration: { xtype: 'tbfill'} The shortcut for a toolbar filler is a hyphen and a greater than symbol: '->' Not all of these shortcuts are documented. So be adventurous, poke around the source code, and see what you can find. Here is a list of the commonly-used shortcuts:
Read more
  • 0
  • 0
  • 7376
Modal Close icon
Modal Close icon