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 - CMS and E-Commerce

830 Articles
article-image-designing-sizing-building-and-configuring-citrix-vdi-box
Packt
13 Sep 2013
7 min read
Save for later

Designing, Sizing, Building, and Configuring Citrix VDI-in-a-Box

Packt
13 Sep 2013
7 min read
(For more resources related to this topic, see here.) Sizing the servers There are a number of tools and guidelines to help you to size Citrix VIAB appliances. Essentially, the guides cover the following topics: CPU Memory Disk IO Storage In their sizing guides, Citrix classifies users into the following two groups: 4kers Knowledge workers Therefore, the first thing to determine is how many of your proposed VIAB users are task workers, and how many are knowledge workers? Task workers Citrix would define task workers as users who run a small set of simple applications, not very graphical in nature or CPU- or memory-intensive, for example, Microsoft Office and a simple line of business applications. Knowledge workers Citrix would define knowledge workers as users who run multimedia and CPU- and memory-intensive applications. They may include large spreadsheet files, graphics packages, video playback, and so on. CPU Citrix offers recommendations based on CPU cores, such as the following: 3 x desktops per core per knowledge worker 6 x desktops per core per task user 1 x core for the hypervisor These figures can be increased slightly if the CPUs have hyper-threading. You should also add another 15 percent if delivering personal desktops. The sizing information has been gathered from the Citrix VIAB sizing guide PDF. Example 1 If you wanted to size a server appliance to support 50 x task-based users running pooled desktops, you would require 50 / 6 = 8.3 + 1 (for the hypervisor) = 9.3 cores, rounded up to 10 cores. Therefore, a dual CPU with six cores would provide 12 x CPU cores for this requirement. Example 2 If you wanted to size a server appliance to support 15 x task and 10 x knowledge workers you would require (15 / 6 = 2.5) + (10 / 3 = 3.3) + 1 (for the hypervisor) = 7 cores. Therefore, a dual CPU with 4 cores would provide 8 x CPU cores for this requirement. Memory The memory required depends on the desktop OS that you are running and also on the amount of optimization that you have done to the image. Citrix recommends the following guidelines: Task worker for Windows 7 should be 1.5 GB Task worker for Windows XP should be 0.5 GB Knowledge worker Windows 7 should be 2 GB Knowledge worker Windows XP should be 1 GB It is also important to allocate memory for the hypervisor and the VIAB virtual appliance. This can vary depending on the number of users, so we would recommend using the sizing spreadsheet calculator available in the Resources section of the VIAB website. However, as a guide, we would allocate 3 GB memory (based on 50 users) for the hypervisor and 1 GB for VIAB. The amount of memory required by the hypervisor will grow as the number of users on the server grows. Citrix also recommends adding 10 percent more memory for server operations. Example 1 If you wanted to size a server appliance to support 50 x task-based users, with Windows 7, you would require 50 x 1.5 + 4 GB (for VIAB and the hypervisor) = 75 GB + 10% = 87 GB. Therefore, you would typically round this up to a 96 GB memory, providing an ideal configuration for this requirement. Example 2 Therefore, if you wanted to size a server appliance to support 15 x task and 10 x knowledge workers, with Windows 7, you would require (15 x 1.5) + (10 x 2) + 4 GB (for VIAB and the hypervisor) = 75 GB + 10% = 51.5 GB. Therefore, a 64 GB memory would be an ideal configuration for this requirement. Disk IO As multiple Windows images run on the appliances, disk IO becomes very important and can often become the first bottleneck for VIAB.Citrix calculates IOPS with a 40-60 split between read and write OPS, during end user desktop access.Citrix doesn't recommend using slow disks for VIAB and has statistic information for SAS 10 and 15K and SSD disks.The following table shows the IOPS delivered from the following disks: Hard drive RPM IOPS RAID 0 IOPS RAID 1 SSD 6000   15000 175 122.5 10000 125 87.7 The following table shows the IOPS required for task and knowledge workers for Windows XP and Windows 7: Desktop IOPS Windows XP Windows 7 Task user 5 IOS 10 IOPS Knowledge user 10 IOPS 20 IOPS Some organizations decide to implement RAID 1 or 10 on the appliances to reduce the chance of an appliance failure. This does require many more disks however, and significantly increases the cost of the solution. SSD SSD is becoming an attractive proposition for organizations that want to run a larger number of users on each appliance. SSD is roughly 30 times faster than 15K SAS drives, so it will eliminate desktop IO bottlenecks completely. SSD continues to come down in price, so can be well worth considering at the start of a VIAB project. SSDs have no moving mechanical components. Compared with electromechanical disks, SSDs are typically less susceptible to physical shock, run more quietly, have lower access time, and less latency. However, while the price of SSDs has continued to decline, SSDs are still about 7 to 8 times more expensive per unit of storage than HDDs. A further option to consider would be Fusion-IO, which is based on NAND flash memory technology and can deliver an exceptional number of IOPS. Example 1 If you wanted to size a server appliance to support 50 x task workers, with Windows 7, using 15K SAS drives, you would require 175 / 10 = 17.5 users on each disk, therefore, 50 / 17. 5 = 3 x 15K SAS disks. Example 2 If you wanted to size a server appliance to support 15 x task workers and 10 knowledge workers, with Windows 7, you would require the following: 175 / 10 = 17.5 task users on each disk, therefore 15 / 17.5 = 0.8 x 15K SAS disks 175 / 20 = 8.75 knowledge users on each disk, therefore 10 / 8.75 = 1.1 x 15K SAS disks Therefore, 2 x 15K SAS drives would be required. Storage Storage capacity is determined by the number of images, number of desktops, and types of desktop. It is best practice to store user profile information and data elsewhere. Citrix uses the following formula to determine the storage capacity requirement: 2 x golden image x number of images (assume 20 GB for an image) 70 GB for VDI-in-a-Box 15 percent of the size of the image / desktop (achieved with linked clone technology) Example 1 Therefore, if you wanted to size a server appliance to support 50 x task-based users, with two golden Windows 7 images, you would require the following: Space for the golden image: 2 x 20 GB x 2 = 80 GB VIAB appliance space: 70 GB Image space/desktop: 15% x 20 GB x 50 = 150 GB Extra room for swap and transient activity: 100 GB Total: 400 GB Recommended: 500 GB to 1 TB per server We have already specified 3 x 15K SAS drives for our IO requirements. If those were 300-GB disks, they should provide enough storage. This section of the article provides you with a step-by-step guide to help you to build and configure a VIAB solution; starting with the hypervisor install. It then goes onto to cover adding an SSL certificate, the benefits of using the GRID IP Address feature, and how you can use the Kiosk mode to deliver a standard desktop to public access areas. It then covers adding a license file and provides details on the useful features contained within Citrix profile management. It then highlights how VIAB can integrate with other Citrix products such as NetScaler VPX, to enable secure connections across the Internet and GoToAssist, a support and monitoring package which is very useful if you are supporting a number of VIAB appliances across multiple sites. ShareFile can again be a very useful tool to enable data files to follow the user, whether they are connecting to a local device or a virtual desktop. This can avoid the problems of files being copied across the network, delaying users. We then move on to a discussion on the options available for connecting to VIAB, including existing PCs, thin clients, and other devices, including mobile devices. The chapter finishes with some useful information on support for VIAB, including the support services included with subscription and the knowledge forums. Installing the hypervisor All the hypervisors have two elements; the bare metal hypervisor that installs on the server and its management tools that you would typically install on the IT administrator workstations. Bare Metal Hypervisor Management tool Citrix XenServer XenCenter Microsoft Hyper-V Hyper V Manager VMware ESXi vSphere Client It is relatively straightforward to install the hypervisor. Make sure you enable linked clones in XenServer, because this is required for the linked clone technology. Give the hypervisor a static IP address and make a note of the administrator's username and password. You will need to download ISO images for the installation media; if you don't already have them, they can be found on the Internet.
Read more
  • 0
  • 0
  • 7821

article-image-searching-data-using-phpmyadmin-and-mysql
Packt
09 Oct 2009
4 min read
Save for later

Searching Data using phpMyAdmin and MySQL

Packt
09 Oct 2009
4 min read
In this article by Marc Delisle, we present mechanisms that can be used to find the data we are looking for instead of just browsing tables page-by-page and sorting them. This article covers single-table and whole database searches. Single-Table Searches This section describes the Search sub-page where single-table search is available. Daily Usage of phpMyAdmin The main use for the tool for some users is with the Search mode, for finding and updating data. For this, the phpMyAdmin team has made it possible to define which sub-page is the starting page in Table view, with the $cfg['DefaultTabTable'] parameter. Setting it to 'tbl_select.php' defines the default sub-page to search. With this mode, application developers can look for data in ways not expected by the interface they are building, adjusting and sometimes repairing data. Entering the Search Sub-Page The Search sub-page can be accessed by clicking the Search link in the Table view. This has been done here for the book table: Selection of Display Fields The first panel facilitates a selection of the fields to be displayed in the results: All fields are selected by default, but we can control-click other fields to make the necessary selections. Mac users would use command-click to select / unselect the fields. Here are the fields of interest to us in this example: We can also specify the number of rows per page in the textbox just next to the field selection. The Add search conditions box will be explained in the Applying a WHERE Clause section later in this article. Ordering the Results The Display order dialog permits to specify an initial sorting order for the results to come. In this dialog, a drop-down menu contains all the table's columns; it's up to us to select the one on which we want to sort. By default, the sorting will be in Ascending order, but a choice of Descending order is available. It should be noted that on the results page, we can also change the sort order. Search Criteria by Field: Query by Example The main usage of the Search panel is to enter criteria for some fields so as to retrieve only the data in which we are interested. This is called Query by example because we give an example of what we are looking for. Our first retrieval will concern finding the book with ISBN 1-234567-89-0. We simply enter this value in the isbn box and choose the = operator: Clicking on Go gives the results shown in the following screenshot. The four fields displayed are those selected in the Select fields dialog: This is a standard results page. If the results ran in pages, we could navigate through them, and edit and delete data for the subset we chose during the process. Another feature of phpMyAdmin is that the fields used as the criteria are highlighted by changing the border color of the columns to better reflect their importance on the results page. It isn't necessary to specify that the isbn column be displayed. We could have selected only the title column for display and selected the isbn column as a criterion. Print View We see the Print view and Print view (with full texts) links on the results page. These links produce a more formal report of the results (without the navigation interface) directly to the printer. In our case, using Print view would produce the following: This report contains information about the server, database, time of generation, version of phpMyAdmin, version of MySQL, and SQL query used. The other link, Print view (with full texts) would print the contents of TEXT fields in its entirety.
Read more
  • 0
  • 0
  • 7720

article-image-creating-new-characters-morphs
Packt
09 Oct 2013
7 min read
Save for later

Creating New Characters with Morphs

Packt
09 Oct 2013
7 min read
(For more resources related to this topic, see here.) Understanding morphs The word morph comes from metamorphosis, which means a change of the form or nature of a thing or person into a completely different one. Good old Franz Kafka had a field day with metamorphosis when he imagined poor Gregor Samsa waking up and finding himself changed into a giant cockroach. This concept applies to 3D modeling very well. As we are dealing with polygons, which are defined by groups of vertices, it's very easy to morph one shape into something different. All that we need to do is to move those vertices around, and the polygons will stretch and squeeze accordingly. To get a better visualization about this process, let’s bring the Basic Female figure to the scene and show it with the wireframe turned on. To do so, after you have added the Basic Female figure, click on the DrawStyle widget on the top-right portion of the 3D Viewport. From that menu, select Wire Texture Shaded. This operation changes how Studio draws the objects in the scene during preview. It doesn't change anything else about the scene. In fact, if you try to render the image at this point, the wireframe will not show up in the render. The wireframe is a great help in working with objects because it gives us a visual representation of the structure of a model. The type of wireframe that I selected in this case is superimposed to the normal texture used with the figure. This is not the only visualization mode available. Feel free to experiment with all the options in the DrawStyle menu; most of them have their use. The most useful, in my opinion, are the Hidden Line, Lit Wireframe, Wire Shaded, and Wire Texture Shaded options. Try the Wire Shaded option as well. It shows the wireframe with a solid gray color. This is, again, just for display purposes. It doesn't remove the texture from the figure. In fact, you can switch back to Texture Shaded to see Genesis fully textured. Switching the view to use the simple wireframe or the shaded wireframe is a Great way of speeding up your workflow. When Studio doesn’t have to render the textures, the Viewport becomes more responsive and all operations take less time. If you have a slow computer, using the wireframe mode is a good way of getting a faster response time. Here are the Wire Texture Shaded and Wire Shaded styles side by side: Now that we have the wireframe visible, the concept of morphing should be simpler to understand. If we pick any vertex in the geometry and we move it somewhere, the geometry is still the same, same number of polygons and same number of vertices, but the shape has shifted. Here is a practical example that shows Genesis loaded in Blender. Blender is a free, fully featured, 3D modeling program. It has extremely advanced features that compete with commercial programs sold for thousands of dollars per license. You can find more information about Blender at http://www.blender.org. Be aware that Blender is a very advanced program with a rather difficult UI. In this image, I have selected a single polygon and pulled it away from the face: In a similar way we can use programs such as modo or ZBrush to modify the basic geometry and come up with all kinds of different shapes. For example, there are people who are specialized in reproducing the faces of celebrities as morph for DAZ V4 or Genesis. What is important to understand about morphs is that they cannot add or remove any portion of the geometry. A morph only moves things around, sometimes to extreme degrees. Morphs for Genesis or Gen4 figures can be purchased from several websites specialized in selling content for Poser and DAZ Studio. In particular, Genesis makes it very easy to apply morphs and even to mix them together. Combining premade morphs to create new faces The standard installation of Genesis provides some interesting ways of changing its shape. Let's start a new Studio scene and add our old friend, the basic Female figure. Once Genesis is in the scene, double-click on it to select it. Now let’s take a look at a new tool, the Shaping tab. It should be visible in the right-hand side pane. Click on the Shaping tab; it should show a list of shapes available. The list should be something like this: As we can see, the Basic Female shape is unsurprisingly dialed all the way to the top. The value of each slider goes from zero, no influence, to one, full influence of the morph. Morphs are not exclusive so, for example, you can add a bit of Body Builder (scroll the list to the bottom if you don't see it) to be used in conjunction with the Basic Female morph. This will give us a muscular woman. This exercise is also giving us an insight about the Basic Female figure that we have used up to this time. The figure is basically the raw Genesis figure with the Basic Female morph applied as a preset. If we continue exploring the Shaping Editor, we can see that the various shapes are grouped by major body section. We have morphs for the shape of the head, the components of the face, the nose, eyes, mouth, and so on. Let's click on the head of Genesis and use the Camera: Frame tool to frame the head in the view. Move the camera a bit so that the face is visible frontally. We will apply a few morphs to the head to see how it can be transformed. Here is the starting point: Now let’s click on the Head category in the Shaping tab. In there we can see a slider labeled Alien Humanoid. Move the slider until it gets to 0.83. The difference is dramatic. Now let’s click on the Eyes category. In there we find two values: Eyes Height and Eyes Width. To create an out-of-this-world creature, we need to break the rules of proportions a little bit, and that means to remove the limits for a couple of parameters. Click on the gear button for the Eyes Height parameter and uncheck the Use Limits checkbox. Confirm by clicking on the Accept button. Once this is done, dial a value of 1.78 for the eyes height. The eyes should move dramatically up, toward the eyebrow. Lastly, let's change the neck; it's much too thick for an alien. Also, in this case, we will need to disable the use of limits. Click on the Neck category and disable the limits for the Neck Size parameter. Once that is done, set the neck size to -1.74. Here is the result, side by side, of the transformation. This is quite a dramatic change for something that is done with just dials, without using a 3D modeling program. It gets even better, as we will see shortly. Saving your morphs If you want to save a morph to re-use it later, you can navigate to File | Save As | Shaping Preset…. To re-use a saved morph, simply select the target figure and navigate to File | Merge… to load the previously saved preset/morph. Why is Studio using the rather confusing term Merge for loading its own files? Nobody knows for sure; it's one of those weird decisions that DAZ's developers made long ago and never changed. You can merge two different Studio scenes, but it is rather confusing to think of loading a morph or a pose preset as a scene merge. Try to mentally replace File | Merge with File | Load. This is the meaning of that menu option.
Read more
  • 0
  • 0
  • 7699

article-image-improving-snake-game
Packt
08 Jul 2013
41 min read
Save for later

Improving the Snake Game

Packt
08 Jul 2013
41 min read
The game Two new features were added to this second version of the game. First, we now keep track of the highest score achieved by a player, saving it through local storage. Even if the player closes the browser application, or turns off the computer, that value will still be safely stored in the player's hard drive, and will be loaded when the game starts again. Second, we use session storage to save the game state every time the player eats a fruit in the game, and whenever the player kills the snake. This is used as an extra touch of awesomeness, where after the player loses, we display a snapshot of all the individual level ups the player achieved in that game, as well as a snapshot of when the player hit a wall or run the snake into itself, as shown in the following screenshot: At the end of each game, an image is shown of each moment when the player acquired a level up, as well as a snapshot of when the player eventually died. This images are created through the canvas API (calling the toDataURL function), and the data that composes each image is saved throughout the game, and stored using the web storage API. With a feature such as this in place, we make the game much more fun, and potentially much more social. Imagine how powerful it would be if the player could post, not only his or her high score to their favorite social network website, but also pictures of their game at key moments. Of course, only the foundation of this feature is implemented in this article (in other words, we only take the snapshots of these critical moments in the game). Adding the actual functionality to send that data to a real social network application is left as an exercise for the reader. A general description and demonstration of each of the APIs used in the game are given in the following sections. For an explanation of how each piece of functionality was incorporated into the final game, look at the code section. For the complete source code for this game, check out the book's page from Packt Publishing's website. Web messaging Web messaging allows us to communicate with other HTML document instances, even if they're not in the same domain. For example, suppose our snake game, hosted at http://snake.fun-html5-games.com, is embedded into a social website through iframe (let's say this social website is hosted at http://www.awesome-html5-games.net). When the player achieves a new high score, we want to post that data from the snake game directly into the host page (the page with iframe from which the game is loaded). With the web messaging API, this can be done natively, without the need for any server-side scripting whatsoever. Before web messaging, documents were not allowed to communicate with documents in other domains mostly because of security. Of course, web applications can still be vulnerable to malicious external applications if we just blindly take messages from any application. However, the web messaging API provides some solid security measures to protect the page receiving the message. For example, we can specify the domains that the message is going to, so that other domains cannot intercept the message. On the receiving end, we can also check the origin from whence the message came, thus ignoring messages from any untrusted domains. Finally, the DOM is never directly exposed through this API, providing yet another layer of security. How to use it Similar to web workers, the way in which two or more HTML contexts can communicate through the web messaging API is by registering an event handler for the on-message event, and sending messages out by using the postMessage function: code1 The first step to using the web messaging API is to get a reference to some document with whom we wish to communicate. This can be done by getting the contentWindow property of an iframe reference, or by opening a new window and holding on to that reference. The document that holds this reference is called the parent document, since this is where the communication is initiated. Although a child window can communicate with its parent, this can only happen when and for as long as this relationship holds true. In other words, a window cannot communicate with just any window; it needs a reference to it, either through a parent-child relationship, or through a child-parent relationship. Once the child window has been referenced, the parent can fire messages to its children through the postMessage function. Of course, if the child window hasn't defined a callback function to capture and process the incoming messages, there is little purpose in sending those messages in the first place. Still, the parent has no way of knowing if a child window has defined a callback to process incoming messages, so the best we can do is assume (and hope) that the child window is ready to receive our messages. The parameters used in the postMessage function are fairly similar to the version used in web workers. That is, any JavaScript value can be sent (numbers, strings, Boolean values, object literals, and arrays, including typed arrays). If a function is sent as the first parameter of postMessage (either directly, or as part of an object), the browser will raise a DATA_CLONE_ERR: DOM Exception 25 error. The second parameter is a string, and represents the domain that we allow our message to be received by. This can be an absolute domain, a forward slash (representing the same origin domain as the document sending the message), or a wild card character (*), representing any domain. If the message is received by a domain that doesn't match the second parameter in postMessage, the entire message fails. When receiving the message, the child window first registers a callback on the message event. This function is passed a MessageEvent object, which contains the following attributes: event.data: It returns the data of the message event.origin: It returns the origin of the message, for server-sent events and cross-document messaging event.lastEventId: It returns the last event ID string, for server-sent events event.sourceReturns: It is the WindowProxy of the source window, for cross-document messaging event.portsReturns: It is the MessagePort array sent with the message, for cross-document messaging and channel messaging Source: http://www.w3.org/TR/webmessaging/#messageevent As an example of the sort of things we could use this feature for in the real world, and in terms of game development, imagine being able to play our snake game, but where the snake moves through a couple of windows. How creative is that?! Of course, in terms of being practical, this may not be the best way to play a game, but I find it hard to argue with the fact that this would indeed be a very unique and engaging presentation of an otherwise common game. With the help of the web messaging API, we can set up a snake, where the snake is not constrained to a single window. Imagine the possibilities when we combine this clever API with another very powerful HTML5 feature, which just happens to lend itself incredibly well to games – web sockets. By combining web messaging with web sockets, we could play a game of snake, not only across multiple windows, but also with multiple players at the same time. Perhaps each player would control the snake when it got inside a given window, and all players could see all windows at the same time, even though they are each using a separate computer. The possibilities are endless, really. Surprisingly, the code used to set up a multi-window port of snake is incredibly simple. The basic setup is the same, we have a snake that only moves in one direction at a time. We also have one or more windows where the snake can go. If we store each window in an array, we can calculate which screen the snake needs to be rendered in, given its current position. Finding out which screen the snake is supposed to be in, given its world position, is the trickiest part. For example, imagine that each window is 200 pixels wide. Now, suppose there are three windows opened. Each window's canvas is only 200 pixels wide as well, so when the snake is at position 350, it would be printed too far to the right in all of the canvases. So what we need to do is first determine the total world width (canvas width multiplied by the total number of canvases), calculate which window the snake is at (position/canvas width), then convert the position from world space down to canvas space, given the canvas the snake is in. First, lets define our structures in the parent document. The code for this is as follows: code2 When this script loads, we'll need a way to create new windows, where the snake will be able to move about. This can easily be done with a button that spawns a new window when clicked, then adding that window to our array of frames, so that we can iterate through that array, and tell every window where the snake is. The code for this is as follows: code3 Now, the real magic happens in the following method. All that we'll do is update the snake's position, then tell each window where the snake is. This will be done by converting the snake's position from world coordinates to canvas coordinates (since every canvas has the same width, this is easy to do for every canvas), then telling every window where the snake should be rendered within a canvas. Since that position is valid for every window, we also tell each window individually whether or not they should render the information we're sending them. Only the window that we calculate the snake is in, is told to go ahead and render. code4 That's really all there is to it. The code that makes up all the other windows is the same for all of them. In fact, we only open a bunch of windows pointing to the exact same script. As far as each window is concerned, they are the only window opened. All they do is take a bunch of data through the messaging API, then render that data if the shouldDraw flag is set. Otherwise, they just clear their canvas, and sit tight waiting for further instructions from their parent window. code5 Web storage Before HTML5 came along, the only way web developers had to store data on the client was through cookies. While limited in scope, cookies did what they were meant to, although they had several limitations. For one thing, whenever a cookie was saved to the client, every HTTP request after that included the data for that cookie. This meant that the data was always explicitly exposed, and each of those HTTP requests were heavily laden with extra data that didn't belong there. This is especially inefficient when considering web applications that may need to store relatively large amounts of data. With the new web storage API, these issues have been addressed and satisfied. There are now three different options for client storage, all of which solve a different problem. Keep in mind, however, that any and all data stored in the client is still exposed to the client in plain text, and is therefore not meant for a secure storage solution. These three storage solutions are session storage, local storage, and the IndexedDB NoSQL data store. Session storage allows us to store key-value data pairs that persist until the browser is closed (in other words, until the session finishes). Local storage is similar to session storage in every way, except that the duration that the data persists is longer. Even when a session is closed, data stored in a local storage still persists. That data in local storage is only cleared when the user specifically tells the browser to do so, or when the application itself deletes data from the storage. Finally, IndexedDB is a robust data store that allows us to store custom objects (not including objects that contains functions), then query the database for those objects. Of course, with much robustness comes great complexity. Although having a dedicated NoSQL database built in right into the browser may sound exciting, but don't be fooled. While using IndexedDB can be a fascinating addition to the world of HTML, it is also by no means a trivial task for beginners. Compared to local storage and session storage, IndexedDB has somewhat of a steep learning curve, since it involves mastering some complex database concepts. As mentioned earlier, the only real difference between local storage and session storage is the fact that session storage clears itself whenever the browser closes down. Besides that, everything about the two is exactly the same. Thus, learning how to use both will be a simple experience, since learning one also means learning the other. However, knowing when to use one over the other might take a bit more thinking on your part. For best results, try to focus on the unique characteristics and needs of your own application before deciding which one to use. More importantly, realize that it is perfectly legal to use both storage systems in the same application. The key is to focus on a unique feature, and decide what storage API best suits those specific needs. Both the local storage and session storage objects are instances of the class Storage. The interface defined by the storage class, through which we can interact with these storage objects, is defined as follows (source: Web Storage W3C Candidate Recommendation, December 08, 2011, http://www.w3.org/TR/webstorage/): getItem(key): It returns the current value associated with the given key. If the given key does not exist in the list associated with the object then this method must return null. setItem(key, value): It first checks if a key/value pair with the given key already exists in the list associated with the object. If it does not, then a new key/value pair must be added to the list, with the given key and with its value set to value. If the given key does exist in the list, then it must have its value updated to value. If it couldn't set the new value, the method must throw a QuotaExceededError exception. (Setting could fail if, for example, the user has disabled storage for the site, or if the quota has been exceeded.) removeItem(key): It causes the key/value pair with the given key to be removed from the list associated with the object, if it exists. If no item with that key exists, the method must do nothing. clear(): It automatically causes the list associated with the object to be emptied of all key/value pairs, if there are any. If there are none, then the method must do nothing. key(n): It returns the name of the nth key in the list. The order of keys is user-agent defined, but must be consistent within an object so long as the number of keys doesn't change. (Thus, adding or removing a key may change the order of the keys, but merely changing the value of an existing key must not.) If n is greater than or equal to the number of key/value pairs in the object, then this method must return null. The supported property names on a Storage object are the keys of each key/value pair currently present in the list associated with the object. length: It returns the number of key/value pairs currently present in the list associated with the object. Local storage The local storage mechanism is accessed through a property of the global object, which on browsers is the window object. Thus, we can access the storage property explicitly through window.localStorage, or implicitly as simply localStorage. code28 Since only DOMString values are allowed to be stored in localStorage, any other values other than strings are converted into a string before being stored in localStorage. That is, we can't store arrays, objects, functions, and so on in localStorage. Only plain JavaScript strings are allowed. code6 Now, while this might seem like a limitation to the storage API, this is in fact done by design. If your goal is to store complex data types for later use, localStorage wasn't necessarily designed to solve this problem. In those situations, we have a much more powerful and convenient storage solution, which we'll look at soon (that is, IndexedDB). However, there is a way to store complex data (including arrays, typed arrays, objects, and so on) in localStorage. The key lies in the wonderful JSON data format. Modern browsers have the very handy JSON object available in the global scope, where we can access two important functions, namely JSON.stringify and JSON.parse. With these two methods, we can serialize complex data, store that in localStorage, then unserialize the data retrieved from the storage, and continue using it in the application. code7 While this is a nice little trick, you will notice what can be a major limitation: JSON stringify does not serialize functions. Also, if you pay close attention to the way that JSON.stringify works, you will realize that>Person, the result will be a simple object literal with no constructor or prototype information. Still, given that localStorage was never intended to fill the role of object persistence (but rather, simple key-value string pairs), this should be seen as nothing more than a limited, yet very neat trick. Session storage Since the sessionStorage interface is identical to that of localStorage, there is no reason to repeat all of the information just described. For a more in-depth discussion about sessionStorage, look at the two previous sections, and replace the word "local" with "session". Everything mentioned above that applies to local storage is also true for session storage. Again, the only difference between the two is that any data saved on sessionStorage is erased when the session with the client ends (that is, whenever the browser is shut down). Some examples of how to use sessionStorage will be shown below. In the example, we will attempt to store a value in the sessionStorage if that value doesn't already exist. Remember, when we set a key-value pair to the storage, if that key already exists in the storage, then whatever value was associated with that key will be overwritten. If the key doesn't exist, it gets created automatically. code8 Note that we can also query the sessionStorage object for a specific key using the in operator, which returns a Boolean value shown as follows: code9 Finally, although we can check the total amount of keys in the storage through sessionStorage.length, that by itself may not be very useful if we don't know what all the different keys are. Thankfully, the sessionStorage.key function allows us to get a specific key, through which we can then get a hold of the value stored with that key. code10 Thus, we can query sessionStorage for a key at a given position, and receive the string key representing that key. Then, with the key we can get a hold of the value stored with that key. Note, however, that the order in which items are stored within the sessionStorage object is totally arbitrary. While some browsers may keep the list of stored items sorted alphabetically by key value, this is clearly specified in the HTML5 spec as a decision to be left up to browser makers. As exciting as the web storage API might seem so far, there are cases when our needs might be such that serializing and unserializing data, as we use local or session storage, might not be quite sufficient. For example, imagine we have a few hundred (or perhaps, several thousand) similar records stored in local storage (say we're storing enemy description cards that are part of an RPG game). Think about how you would do the following using local storage: Retrieve, in alphabetical order, the first five records stored Delete all records stored that contain a particular characteristic (such as an enemy that doesn't survive in water, for example) Retrieve up to three records stored that contain a particular characteristic (for example, the enemy has a Hit Point score of 42,000 or more) The point is this: any querying that we may want to make against the data stored in local storage or session storage, must be handled by our own code. In other words, we'd be spending a lot of time and effort writing code just to help us get to some data. Let alone the fact that any complex data stored in local or session storage is converted to literal objects, and any and all functions that were once part of those objects are now gone, unless we write even more code to handle some sort of custom unserializing. In case you have not guessed it by now, IndexedDB solves these and other problems very beautifully. At its heart, IndexedDB is a NoSQL database engine that allows us to store whole objects and index them for fast insertions, deletions, and retrievals. The database system also provides us with a powerful querying engine, so that we can perform very advanced computations on the data that we have persisted. The following figure shows some of the similarities between IndexedDB and a traditional relational database. In relational databases, data is stored as a group of rows within a specific table structure. In IndexedDB, on the other hand, data is grouped in broadly-defined buckets known as data stores. The architecture of IndexedDB is somewhat similar to the popular relational database systems used in most web development projects today. One core difference is that, whereas relational databases store data in a database, which is a collection of related tables, an IndexedDB system groups data in databases, which is a collection of data stores. While conceptually similar, in practice these two architectures are actually quite different. Note If you come from a relational database background, and the concept of databases, tables, columns, and rows makes sense to you, then you're well on your way to becoming an IndexedDB expert. As you'll see, there are some significant distinctions between both systems and methodologies. While you might be tempted to simply replace the words data store with tables, know that the difference between the two concepts extends beyond a name difference. One key feature of data stores is that they don't have any specific schema associated with them. In relational databases, a table is defined by its very particular structure. Each column is specified ahead of time, when the table is first created. Then, every record saved in such a table follows the exact same format. In NoSQL databases (which IndexedDB is a type of), a data store can hold any object, with whatever format they may have. Essentially, this concept would be the same as having a relational database table that has a different schema for each record in it. IDBFactory To get started with IndexedDB, we first need to create a database. This is done through an implementation of IDBFactory, which in the browser, is the window.indexedDB object. Deleting a database is also done through the indexedDB object, as we'll see soon. In order to open a database (or create one if it doesn't exist yet), we simply call the indexedDB.open method, passing in a database name, along with a version number. If no version number is supplied, the default version number of one will be used as shown in the following code snippet: code11 As you'll soon notice, every method for asynchronous requests in IndexedDB (such as indexedDB.open, for example), will return a request object of type IDBRequest, or an implementation of it. Once we have that request object, we can set up callback functions on its properties, which get executed as the various events related to them are fired, as shown in the following code snippet: code12 IDBOpenDBRequest As mentioned in the previous section, once we make an asynchronous request to the IndexedDB API, the immediately returned object will be of type IDBRequest. In the particular case of an open request, the object that is returned to us is of type IDBOpenDBRequest. Two events that we might want to listen to on this object were shown in the preceding code snippet (onerror and onsuccess). There is also a very important event, wherein we can create an object store, which is the foundation of this storage system. This event is the onupgradeneeded (that is, on upgrade needed) event. This will be fired when the database is first created and, as you might expect, whenever the version number used to open the database is higher than the last value used when the database was opened, as shown in the following code: code13 The call to createObjectStore made on the database object takes two parameters. The first is a string representing the name of the object store. This store can be thought of as a table in the world of relational databases. Of course, instead of inserting records into columns from a table, we insert whole objects into the data store. The second parameter is an object defining properties of the data store. One important attribute that this object must define is the keyPath object, which is what makes each object we store unique. The value assigned to this property can be anything we choose. Now, any objects that we persist in this data store must have an attribute with the same name as the one assigned to keyPath. In this example, our objects will need to have an attribute of myKey. If a new object is persisted, it will be indexed by the value of this property. Any additional objects stored that have the same value for myKey will replace any old objects with that same key. Thus, we must provide a unique value for this object every time we want a unique object persisted. Alternatively, we can let the browser provide a unique value for this key for us. Again, comparing this concept to a relational database, we can think of the keyPath object as being the same thing as a unique ID for a particular element. Just as most relational database systems will support some sort of auto increment, so does IndexedDB. To specify that we want auto-incremented values, we simply add the flag to the object store properties object when the data store is first created (or upgraded) as shown in the following code snippet: code14 Now we can persist an object without having to provide a unique value for the property myKey. As a matter of fact, we don't even need to provide this attribute at all as part of any objects we store here. IndexedDB will handle that for us. Take a look at the following diagram: Using Google Chrome's developer tools, we can see all of the databases and data stores we have created for our domain. Note that the primary object key, which has whatever name we give it during the creation of our data store, has IndexedDB-generated values, which, as we have specified, are incremented over the last value. With this simple, yet verbose boilerplate code in place, we can now start using our databases and data stores. From this point on, the actions we take on the database will be done on the individual data store objects, which are accessed through the database objects that created them. IDBTransaction The last general thing we need to remember when dealing with IndexDB, is that every interaction we have with the data store is done inside transactions. If something goes wrong during a transaction, the entire transaction is rolled back, and nothing takes effect. Similarly, if the transaction is successful, IndexedDB will automatically commit the transaction for us, which is a pretty handy bonus. To use transaction, we need to get a reference to our database, then request a transaction for a particular data store. Once we have a reference to a data store, we can perform the various functions related to the data store, such as putting data into it, reading data from it, updating data, and finally, deleting data from a data store. code15 To store an item in our data store we need to follow a couple of steps. Note that if anything goes wrong during this transaction, we simply catch whatever error is thrown by the browser, and execution continues uninterrupted because of the try/catch block. The first step to persisting objects in IndexedDB is to start a transaction. This is done by requesting a transaction object from the database we have opened earlier. A transaction is always related to a particular data store. Also, when requesting a transaction, we can specify what type of transaction we'd like to start. The possible types of transactions in IndexedDB are as follows: readwrite This transaction mode allows for objects to be stored into the data store, retrieved from it, updated, and deleted. In other words, readwrite mode allows for full CRUD functionality. readonly This transaction mode is similar to readwrite, but clearly restricts the interactions with the data store to only reading. Anything that would modify the data store is not allowed, so any attempt to create a new record (in other words, persisting a new object into the data store), update an existing object (in other words, trying to save an object that was already in the data store), or delete an object from the data store will result in the transaction failing, and an exception being raised. versionchange This transaction mode allows us to create or modify an object store or indexes used in the data store. Within a transaction of this mode, we can perform any action or operation, including modifying the structure of the database. Getting elements Simply storing data into a black box is not at all useful if we're not able to retrieve that data at a later point in time. With IndexedDB, this can be done in several different ways. More commonly, the data store where we persist the data is set up with one or more indexes, which keep the objects organized by a particular field. Again, for those accustomed to relational databases, this would be similar to indexing/applying a key to a particular table column. If we want to get to an object, we can query it by its unique ID, or we can search the data store for objects that fit particular characteristics, which we can do through indexed values of that object. To create an index on a data store, we must specify our intentions during the creation of the data store (inside the onupgradeneeded callback when the store is first created, or inside a transaction mode versionchange). The code for this is as follows: code16 In the preceding example, we create an index for the task attribute of our objects. The name of this index can be anything we want, and commonly is the same name as the object property to which it applies. In our case, we simply named it taskIndex. The possible settings we can configure are as follows: unique – if true, an object being stored with a duplicate value for the same attribute is rejected multiEntry – if true, and the indexed attribute is an array, each element will be indexed Note that zero or more indexes can be created for a data store. Just like any other database system, indexing your database/data store can really boost the performance of the storage container. However, just adding indexes for the fun it provides is not a good idea, as the size of your data store will grow accordingly. A good data store design is one where the specific context of the data store with respect to the application is taken into account, and each indexed field is carefully considered. The phrase to keep in mind when designing your data stores is the following: measure it twice, cut it once. Although any object can be saved in a data store (as opposed to a relational database, where the data stored must carefully follow the table structure, as defined by the table's schema), in order to optimize the performance of your application, try to build your data stores with the data that it will store in mind. It is true that any data can be smacked into any data store, but a wise developer considers the data being stored very carefully before committing it to a database. Once the data store is set up, and we have at least one meaningful index, we can start to pull data out of the data store. The easiest way to retrieve objects from a data store is to use an index, and query for a specific object, as shown in the following code: code17 The preceding function attempts to retrieve a single saved object from our data store. The search is made for an object with its task property that matches the task name supplied to the function. If one is found, it will be retrieved from the data store, and passed to the store object's request through the event object passed in to the callback function. If an error occurs in the process (for example, if the index supplied doesn't exist), the onerror event is triggered. Finally, if no objects in the data store match the search criteria, the resulting property passed in through the request parameter object will be null. Now, to search for multiple items, we can take a similar approach, but instead we request an IndexedDBCursor object. A cursor is basically a pointer to a particular result from a result set of zero or more objects. We can use the cursor to iterate through every object in the result set, until the current cursor points at no object (null), indicating that there are no more objects in the result set. code18 You will note a few things with the above code snippet. First, any object that goes into our IndexedDB data store is stripped of its DNA, and only a simple hash is stored in its stead. Thus, if the prototype information of each object we retrieve from the data store is important to the application, we will need to manually reconstruct each object from the data that we get back from the data store. Second, observe that we can filter the subset of the data store that we would like to take out of it. This is done with an IndexedDB Key Range object, which specifies the offset from which to start fetching data. In our case, we specified a lower bound of zero, meaning that the lowest primary key value we want is zero. In other words, this particular query requests all of the records in the data store. Finally, remember that the result from the request is not a single result or an array of results. Instead, all of the results are returned one at a time in the form of a cursor. We can check for the presence of a cursor altogether, then use the cursor if one is indeed present. Then, the way we request the next cursor is by calling the continue() function on the cursor itself. Another way to think of cursors is by imagining a spreadsheet application. Pretend that the 10 objects returned from our request each represent a row in this spreadsheet. So IndexedDB will fetch all 10 of those objects to memory, and send a pointer to the first result through the event.target.result property in the onsuccess callback. By calling cursor.continue(), we simply tell IndexedDB to now give us a reference to the next object in the result set (or, in other words, we ask for the next row in the spreadsheet). This goes on until the tenth object, after which no more objects exist in the result set (again, to go along with the spreadsheet metaphor, after we fetch the last row, the next row after that is null – it doesn't exist). As a result, the data store will call the onsuccess callback, and pass in a null object. If we attempt to read properties in this null reference, as though we were working with a real object returned from the cursor, the browser will throw a null pointer exception. Instead of trying to reconstruct an object from a cursor one property at a time, we could abstract this functionality away in a generic form. Since objects being persisted into the object store can't have any functions, we're not allowed to keep such functionality inside the object itself. However, thanks to JavaScript's ability to build an object from a reference to a constructor function, we can create a very generic object builder function as follows: code19 Deleting elements To remove specific elements from a data store, the same principles involved in retrieving data apply. In fact, the entire process looks fairly identical to retrieving data, only we call the delete function on the object store object. Needless to say, the transaction used in this action must be readwrite, since readonly limits the object so that no changes can be done to it (including deletion). The first way to delete an object is by passing the object's primary key to the delete function. This is shown as follows: code20 The difficulty with this first approach is that we need to know the ID of the object. In some cases, this would involve a prior transaction request where we'd retrieve the object based on some easier to get data. For example, if we want to delete all tasks with the attribute of complete set to true, we'd need to query the data store for those objects first, then use the IDs associated with each result, and use those values in the transaction where the objects are deleted. A second way to remove data from the data store is to simply call clear() on the object store object. Again, the transaction must be set to readwrite. Doing this will obliterate every last object in the data store, even if they're all of different types as shown in the following code snippet: code21 Finally, we can delete multiple records using a cursor. This is similar to the way we retrieve objects. As we iterate through the result set using the cursor, we can simply delete the object at whatever position the cursor is currently on. Upon deletion, the reference from the cursor object is set to null as shown in the following code snippet: code22 This is pretty much the same routine as fetching data. The only detail is that we absolutely need to supply an object's key. The key is the value stored in the object's keyPath attribute, which can be user-provided, or auto-generated. Fortunately for us, the cursor object returns at least two references to this key through the cursor.primaryKey property, as well as through the object's own property that references that value (in our case, we chose the keyPath attribute to be named myKey). The two upgrades we added to this second version of the game are simple, yet they add a lot of value to the game. We added a persistent high score engine, so users can actually keep track of their latest record, and have a sticky record of past successes. We also added a pretty nifty feature that takes a snapshot of the game board each time the player scores, as well as whenever the player ultimately dies out. Once the player dies, we display all of the snapshots we had collected throughout the game, allowing the player to save those images, and possibly share it with his or her friends. Saving the high score The first thing you probably noticed about the previous version of this game was that we had a placeholder for a high score, but that number never changed. Now that we know how to persist data, we can very easily take advantage of this, and persist a player's high score through various games. In a more realistic scenario, we'd probably send the high score data to a backend server, where every time the game is served, we can keep track of the overall high score, and every user playing the game would know about this global score. However, in our situation, the high score is local to a browser only, since none of the persistence APIs (local and session storage, as well as IndexedDB) share data across other browsers, or natively to a remote server. Since we want the high score to still exist in a player's browser even a month from now, after the computer has been powered off (along with the browser, of course) multiple times, storing this high score data on sessionStorage would be silly. We could store this single number either in IndexedDB or in localStorage. Since we don't care about any other information associated with this score (such as the date when the score was achieved, and so on), all we're storing really is just the one number. For this reason, I think localStorage is a much better choice, because it can all be done in as few as 5 lines of code. Using IndexedDB would work, but would be like using a cannon to kill a mosquito: code23 This function is pretty straight forward. The two values we pass it are the actual score to set as the new high score (this value will be both saved to localStorage, as well as displayed to the user), and the HTML element where the value will be shown. First, we retrieve the existing value saved under the key high-score, and convert it to a number. We could have used the function parseInt(), but multiplying a string by a number does the same thing, but with a slightly faster execution. Next, we check if that value evaluated to something real. In other words, if there was no high-score value saved in local storage, then the variable score would have been evaluated to undefined multiplied by one, which is not a number. If there is a value saved with the key high-score, but that value is not something that can be converted into a number (such as a string of letters and such), we know that it is not a valid value. In this case, we set the incoming score as the new high score. This would work out in the case where the current persisted value is invalid, or not there (which would be the case the very first time the game loads). Next, once we have a valid score retried from local storage, we check if the new value is higher than the old, persisted value. If we have a higher score, we persist that value, and display it to the screen. If the new value is not higher than the existing value, we don't persist anything, but display the saved value, since that is the real high score at the time. Taking screenshots of the game This feature is not as trivial as saving the user's high score, but is nonetheless very straightforward to implement. Since we don't care about snapshots that we captured more than one game ago, we'll use sessionStorage to save data from the game, in real time as the player progresses. Behind the scenes, all we do to take these snapshots is save the game state into sessionStorage, then at the end of the game we retrieve all of the pieces that we'd been saving, and reconstruct the game at those points in time into an invisible canvas. We then use the canvas.toDataURL() function to extract that data as an image: code24 Each time the player eats a fruit, we call this function, passing it a reference to the snake (our hero in this game), and the fruit (the goal of this game) objects. What we do is really quite simple: we create an array representing the state of the snake and of the fruit at each event that we capture. Each element in this array is a string representing the serialized array that keeps track of where the fruit was, and where each body part of the snake was located as well. First, we check if this object currently exists in sessionStorage. For the first time we start the game, this object will not yet exist. Thus, we create an object that references those two objects, namely the snake and the fruit object. Next, we stringify the buffers keeping track of the locations of the elements we want to track. Each time we add a new event, we simply append to those two buffers. Of course, if the user closes down the browser, that data will be erased by the browser itself, since that's how sessionStorage works. However, we probably don't want to hold on to data from a previous game, so we also need a way to clear out our own data after each game. code25 Easy enough. All we need is to know the name of the key that we use to hold each element. For our purposes, we simply call the snapshots of the snake eating "eat", and the buffer with the snapshot of the snake dying "die". So before each game starts, we can simply call clearEvent() with those two global key values, and the cache will be cleared a new each time. Next, as each event takes place, we simply call the first function we defined, sending it the appropriate data as shown in the following code snippet: code26 Finally, whenever we wish to display all of these snapshots, we just need to create a separate canvas with the same dimensions as the one used in the game (so that the buffers we saved don't go out of bounds), and draw the buffers to that canvas. The reason we need a separate canvas element is because we don't want to draw on the same canvas that the player can see. This way, the process of producing these snapshots is more seamless and natural. Once each state is drawn, we can extract each image, resize it, and display it back to the user as shown in the following code: code27 Observe that we simply draw the points representing the snake and the fruit into that canvas. All of the other points in the canvas are ignored, meaning that we generate a transparent image. If we want the image to have an actual background color (even if it is just white), we can either call fillRect() over the entire canvas surface before drawing the snake and the fruit, or we can traverse each pixel in the pixelData array from the rendering context, and set the alpha channel to 100 percent opaque. Even if we set a color to each pixel by hand, but leave off the alpha channel, we'd have colorful pixels, but 100 percent transparent. Summary In this article we took a few extra steps into the fascinating world of 2D rendering using the long-awaited canvas API. We took advantage of the canvas' ability to export images to make our game more engaging, and potentially more social. We also made the game more engaging and social by adding a persistence layer on top of the game, whereby we were able to save a player's high score. Two other new powerful features of HTML5, web messaging and IndexedDB, were explored in this article, although there were no uses for these features in this version of the game. The web messaging API provides a mechanism for two or more windows to communicate directly through message passing. The exciting bit is that these windows (or HTML contexts) do not need to be in the same domain. Although this could sound like a security issue, there are several systems in place to ensure that cross-document and cross-domain messaging is secure and efficient. The web storage interface brings with it three distinct solutions for long term data persistence on the client. These are session storage, local storage, and IndexedDB. While IndexedDB is a full-blown, built-in, fully transactional and asynchronous NoSQL object store, local and session storage provide a very simple key-value pair storage for simpler needs. All three of these systems introduce great benefits and gains over the traditional cookie-based data storage, including the fact that the total amount of data that can be persisted in the browser is much greater, and none of the data saved in the user's browser ever travels back and forth between the server and the client through HTTP requests. Resources for Article :   Further resources on this subject: Interface Designing for Games in iOS [Article] Unity 3D Game Development: Don't Be a Clock Blocker [Article] Making Money with Your Game [Article]
Read more
  • 0
  • 0
  • 7653

article-image-call-control-using-3cx
Packt
11 Feb 2010
9 min read
Save for later

Call Control using 3CX

Packt
11 Feb 2010
9 min read
Let's get started! Ring groups Ring groups are designed to direct calls to a group of extensions so that a person can answer the call. An incoming call will ring at several extensions at once, and the one who picks up the phone gets control of that call. At that point, he/she can transfer the call, send it to voicemail, or hang up. Ring groups are my preferred call routing method. Does anyone really like those automated greetings? I don't. We will of course, set those up because they do have some great uses. However, if you like your customers to get a real live voice when they call, you have two choices—either direct the call to an extension or use a ring group and have a few phones ring at once. To create a ring group, we will use the 3CX web interface. There are several ways to do this. From the top toolbar menu, click Add | Ring Group. In the following screenshot, I chose Add | Ring Group: The following screenshot shows another way of adding a ring group using the Ring Groups section in the navigation pane on the left-hand side. Then click on the Add Ring Group button on the toolbar: Once we click Add Ring Group, 3CX will automatically create a Virtual machine number for this ring group as shown in the next screenshot. This helps the system keep track of calls and where they are. This number can be changed to any unused number that you like. As a reseller, I like to keep them the same from client to client. This creates some standardization among all the systems. Now it's time to give the ring group a Name. Here I use MainRingGroup as it lets me know that when a call comes in, it should go to the Main Ring Group. After you create the first one, you can make more such as SalesRingGroup, SupportRingGroup, and so on. We now have three choices for the Ring Strategy: Prioritized Hunt: Starts hunting for a member from the top of the Ring Group Members list and works down until someone picks up the phone or goes to the Destination if no answer section. Ring All: If all the phones in the Ring Group Members section ring at the same time then the first person to pick up gets the call. Paging: This is a paid feature that will open the speakerphone on Ring Group Members. Now you will need to select your Ring Time (Seconds) to determine how long you want the phones to ring before giving up. The default ring time is 20 seconds, which all my clients agree is too long. I'd recommend 10-15 seconds, but remember, if no one picks up the phone, then the caller goes to the next step, such as a Digital Receptionist. If the next step also makes the caller wait another 10-20 seconds, he/she may just hang up. You also need to be sure that you do not exceed the phone company's timeout of diverting calls to their voicemail (which could be turned off) or returning a busy signal. Adding ring group members Ring Group Members are the extensions that you would like the system to call or page in a ring group. If you select the Prioritized Hunt strategy, it will hunt from the top and go down the list. Ring All and Paging will get everyone at once. The listbox on the left will show you a list of available extensions. Select the ones you want and click the Add button. If you are using Prioritized Hunt, you can change the order of the hunt by using the Up and Down buttons. Destination if no answer The last setting as shown in the next screenshot illustrates what to do when no one answers the call. The options are as follows: End Call: Just drop the call, no chance for the caller to talk to someone. Connect to Extension: Ring the extension of your choice. Connect to Queue / Ring Group: This sends the caller to a call queue (discussed later in the Call queues section)) or to another ring group. A second ring group could be created for stage two that calls the same group plus additional extensions. Connect to Digital Receptionist: As a person didn't pick up the call, we can now send it to an automated greeting/menu system. Voicemail box for Extension: As the caller has already heard phones ringing, you may just want to put him/her straight to someone's voicemail. Forward to Outside Number: If you have had all the phones in the building ringing and no one has picked up, then you might want to send the caller to a different phone outside of your PBX system. Just make sure that you enter the correct phone number and any area codes that may be required. This will use another simultaneous call license and another phone line. If you have one line only, then this is not the option you can use. Digital Receptionist setup A Digital Receptionist (DR) is not a voicemail box; it's an automated greeting with a menu of choices to choose from. A DR will answer the phone for you if no one is available to answer the phone (directly to an extension or hunt group) or if it is after office hours. You need to set up a DR unless you want all incoming calls to go to someone's voicemail. You will also need it if you want to present the caller with a menu of options. Let's see how to create a DR. Recording a menu prompt The first thing you need to do in order to create a DR is record a greeting. There are a couple of ways to do this. However, first let's create the greeting script. In this greeting, you will be defining your phone menu; that is, you will be directing calls to extensions, hunts, agent groups, and the dial by name directory. Following is an example: Thank you for calling. If you know your party's extension, you may dial it at any time. Or else, please listen to the following options: For Rob, dial 1 For the sales group, dial 2 For Zachary, dial 4 Solicitors, please dial 8 For a dial by name directory, dial 9 I suggest having it written down. This makes it easier to record and also gives the person setting up the DR in 3CX a copy of the menu map. Now that you know what you want your callers to hear when they call, it's time to get it recorded so that we can import it into 3CX. You have a couple of options for recording the greeting script. It doesn't matter which option you use or how you obtain this greeting file, as long as the end format is correct. You can hire a professional announcer, put it to music, and obtain the file from him/her. You can record it using any audio software you like such as Windows Sound Recorder, or any audio recording software. The file needs to be a .wav or an .mp3 file saved in PCM, 8KHz, 16 bit, Mono format. If you have Windows Sound Recorder only, I'd suggest that you try out Audacity. Audacity is an open source audio file program available at http://audacity.sourceforge.net/. Audacity gives you a lot more power such as controlling volume, combining several audio tracks (a music track to go with the announcer), using special effects, and many other cool audio tools. I'm not an expert in it but the basics are easy to do. First, hit the Audacity website and download it, then install it using the defaults. Now let's launch Audacity and set it up to use the correct file format, which will save us any issues later. Start by clicking Edit | Preferences. On the Quality tab, select the Default Sample Rate as 8000 Hz. Then change the Default Sample Format to 16-bit as shown in the following screenshot: Now, on the File Formats tab, select WAV (Microsoft 16 bit PCM) from the drop-down list and click OK: Now that those settings are saved, you can record your greeting without having to change any formats. Now it's time to record your greeting. Click on the red Record button as shown in the following screenshot. It will now use your PC's microphone to record the announcer's voice and when the recording is done, click on the Stop button. Press Play to hear it, and if you don't like it, start over again: If you like the way your greeting sounds, then you will need to save it. Click File | Export As WAV... or Export As MP3.... Save it to a location that you remember (for example, c:3CX prompts is a good place) with a descriptive filename. While you are recording this greeting, you might as well record a few more if you have plans for creating multiple DRs: Creating the Digital Receptionist With your greeting script in hand, it's time to create your first DR. In the navigation pane on the left side, click Digital Receptionist, then click Add Digital Receptionist as shown in the following screenshot: Or on the top menu toolbar, click Add | Digital Receptionist: Just like your ring group, the DR gets a Virtual extension number by default, Feel free to change it or stick with it. Give it a Name, (I like to use the same name as the audio greeting filename.) Now, click Browse... and then Add. Browse to your c:3CX prompts directory and select your .wav or .mp3 file as shown in the following screenshot: Next, we need to create the menu system as shown in the following screenshot. We have lots of options available. You can connect to an extension or ring group, transfer directly to someone's voicemail, end the call (my solicitors' option), or start the call by name feature (discussed in the Call by name setup section). At any time during playback, callers can dial the extension number; they don't have to hear all the options. I usually explain this in the DR recorded greeting.
Read more
  • 0
  • 0
  • 7649

article-image-drupal-6-theming-adding-and-optimizing-css-files
Packt
19 Nov 2010
8 min read
Save for later

Drupal 6 Theming: Adding and Optimizing CSS Files

Packt
19 Nov 2010
8 min read
Drupal 6 Theming Cookbook Over 100 clear step-by-step recipes to create powerful, great-looking Drupal themes Take control of the look and feel of your Drupal website Tips and tricks to get the most out of Drupal's theming system Learn how to customize existing themes and create unique themes from scratch Part of Packt's Cookbook series: Each recipe is a carefully organized sequence of instructions to complete the task as efficiently as possible Including a CSS file in a theme This recipe details the steps involved in adding a CSS file to the theme via its .info file. In this case, we will be adding a CSS file to the mytheme sub-theme which we created in the previous article. Getting ready Create a CSS file inside the theme's folder named mytheme.css and add the following example rule to it: * { color: #996633 !important; } This rule should override and change the color of all text on the page to a brownish hue. How to do it... Adding a CSS file to a theme is best accomplished via its .info file. Navigate to the theme's folder at sites/all/themes/mytheme and open the mytheme.info file in an editor. Add the following line to this file to include our CSS: stylesheets[all][] = mytheme.css If the CSS file is stored along with other stylesheets in a sub-folder named css, the syntax would include the relative path to the file as follows: stylesheets[all][] = css/mytheme.css. Once done, save the file and exit the editor. Since we have modified the .info file and introduced a new file, our changes will not take effect until the theme registry is rebuilt. Therefore, clear the Drupal cache and view the site to confirm that our new stylesheet has been included correctly. The theme should now display all text in brown. How it works... Drupal checks the .info file and notes that we have declared stylesheets using the stylesheets variable. The syntax of this variable is similar to that of an array in PHP. The all index in the syntax represents the media type as used in CSS declarations. The next screenshot displays a section of the source code of a page which confirms the inclusion of the new stylesheet, mytheme.css. We can also see that our sub-theme is including the stylesheets declared by its base theme—Garland—as well as its own stylesheets. In the preceding screenshot, we can see that Drupal references each stylesheet along with a query string. For example, mytheme.css is included as mytheme.css?e. This rather quirky suffix is a trick used by Drupal to ensure that browsers do not use stale copies of a cached CSS file while rendering our site. We can test this by clearing the Drupal cache and viewing the source code once again. Now, our stylesheets should have a different suffix— perhaps, something like mytheme.css?A—thereby tricking browsers into believing that these are different files and using them instead of their cached copies. There's more... One of the advantages of using a sub-theme is that we can easily override elements of the base theme. This includes stylesheets as well. Overriding the base theme's stylesheet If the base theme includes a stylesheet named layout.css, adding a stylesheet of the same name in the sub-theme will override the base theme's stylesheet. In other words, Drupal will include the sub-theme's stylesheet instead of that of the base theme. Enabling CSS optimization CSS optimization in Drupal is accomplished through two steps—aggregation and compression. This optimization provides a significant boost to performance both on the server as well as for the user. This recipe details the steps to be performed to enable this feature in Drupal. Getting ready CSS optimization is a requirement only when a site is ready to go live. Until such time, it is recommended that it be left switched off as CSS changes during development will not take effect unless the Drupal cache is cleared. How to do it... Optimization and other performance-related features are sequestered within admin/ settings/performance (Home | Administer | Site configuration | Performance). This performance configuration page should have a section titled Bandwidth optimizations which should contain options for CSS and Javascript optimization. Look for the setting named Optimize CSS files and set it to Enabled as in the following screenshot: Public File system As the screenshot states, the optimized CSS file is cached using Drupal's file system which needs to be set to public to ensure that the user's browser can access and download it. Therefore, it is necessary to set Download method of the Drupal file system to Public. This can be done via admin/settings/file-system (Home | Administer | Site configuration | File system). Once done, click on the Save configuration button at the bottom of the page to save our changes. How it works... Aggregation involves the collating and joining of multiple CSS files into a single stylesheet, while compression reduces the resulting file to a smaller size by trimming out unnecessary elements such as whitespace. The former helps in reducing the number of files that the server has to load and serve. The latter saves on bandwidth and time. The previous and following screenshots demonstrate CSS optimization at work. The previous screenshot is a snippet of the HTML source of a Drupal page running on a stock Garland theme. As displayed, this involves the server performing look-ups and serving eight separate CSS files—seven for all media types and a print stylesheet—for each and every page served. If this is extrapolated to sites of greater complexity, the number of files and, consequently, the server and bandwidth load, begin to take on significant proportions and can seriously impact performance. The preceding screenshot is of the same page as before with one difference—CSS optimization is now turned on. The number of CSS files has now been reduced to only two—one for all media types and the other being the print media type. These stylesheets are stored in the files folder and are cached copies. As a result, each page load now only involves the webserver serving two files instead of the previous eight. There's more... CSS optimization and other performance improvements should be used with care. When to use it CSS optimization is only necessary to improve performance on production sites. Enabling it beforehand will only hinder theme development. Enabling optimization can sometimes be handy when working on sites which are using more than 31 stylesheets—a not too infrequent occurrence on sites using a plethora of modules and an elaborate theme—as this is an upper-bound for Internet Explorer. IE will only load the first 31 stylesheets and ignore the rest. Drupal's CSS optimization feature reduces this number to one, thereby conveniently working around the issue. An alternative is to use modules such as IE CSS Optimizer (http://drupal.org/project/ie_css_optimizer). Other optimizations Other optimization settings can also be configured on the performance page. These include page caching, block caching, and JavaScript optimization. It is also worthwhile browsing the caching and performance modules that are available as contributed modules via http:// drupal.org/project/modules under the category Performance and scalability. Creating the mysite module to hold our tweaks In the course of developing our site, we will frequently come across situations where various elements of the site need to be tweaked in PHP using Drupal's APIs. While a lot of theme-specific cases can be stored in template files, certain tweaks which are theme-agnostic require that we store them in a module to ensure that they are available to all themes. This recipe covers the creation of a module to hold all these bits and pieces. Getting ready Create a folder inside sites/all named modules. This is where custom and contributed modules are usually housed. How to do it... The following list details the procedure involved in creating a module named mysite to hold our theme-agnostic customizations and other odds and ends: Create a folder inside sites/all/modules named mysite where mysite refers to the name of our site. Create a file named mysite.info within the mysite folder. Edit this file and add the following code inside: name = Mysite description = A module to hold odds and ends for mysite. core = 6.x Save the file. Create another file named mysite.module which will hold our odds and ends. Save and exit the editor. Finally, enable the module via the module administration page at admin/build/ modules (Home | Administer | Site building | Modules). How it works... Just as with themes, modules require a .info file which provides information to Drupal on compatibility, dependencies, and so on. Once Drupal ascertains that the module is compatible with the version installed, it loads the .module file of the same name and processes it accordingly. We can test if the module is working by adding a snippet such as the following: <?php /** * Implementation of hook_init(). */ function mysite_init(){ // Display a message on every page load. drupal_set_message("Welcome to MySite!"); } As the comment suggests, the preceding snippet will display a welcome message on every page load. There's more... The Drupal community routinely comes up with modules to ease the pain of development. Module builder There's a module available named Module builder which can be used to generate a skeleton of a general module. This can subsequently be populated as per our requirements. It is available at http://drupal.org/project/module_builder.
Read more
  • 0
  • 0
  • 7646
Unlock access to the largest independent learning library in Tech for FREE!
Get unlimited access to 7500+ expert-authored eBooks and video courses covering every tech area you can think of.
Renews at $19.99/month. Cancel anytime
article-image-backing-and-restoring-typo3-websites
Packt
22 Oct 2009
4 min read
Save for later

Backing Up and Restoring TYPO3 Websites

Packt
22 Oct 2009
4 min read
What Needs Backing Up in TYPO3? We need to back up: The TYPO3 files A copy of the database These two things make up our TYPO3 installation. We need the database as it contains the website's content and records of the website's users. We need the TYPO3 files as they contain the website's settings in the configuration files, copies of the website's design, and copies of data that has been cached by TYPO3. Backing Up the TYPO3 Files Depending on the operating system we are using, there are a number of different ways in which we can back up the files from TYPO3. In this article, we will look into backing up the files on Windows and on Linux. This is because Windows is the most used operating system, and Linux is the most popular hosting environment for websites. Backing Up Our Files on Windows In Windows, we can easily create a compressed file containing all the TYPO3 files (known as a ZIP file), using the Windows Compressed Folder tool, or a program such as WinZip. Provided we've used the default installation path, TYPO3 will be located in the folder C:Program FilesTypo3_4.0.2Apache and the folder that we want to compress is typo3_src. We could just back up the fileadmin, typo3conf, and uploads folders. This way, should we lose our entire website, we can simply restore the whole thing instead of having to restore TYPO3 and then our extra TYPO3 files. Now that we have a backup, we should copy it to a separate location (preferably on an external disk, or on another computer) for safe keeping. Backing Up Our Files on Linux or Linux Hosting We can create a complete backup of our home directory on a Linux hosting environment. This home directory contains all of our files on the hosting account. Alternatively, we can run a simple command to compress a particular folder. If we have a web hosting account that provides us with access to the cPanel hosting control panel, we can use that to generate a backup of our entire website (except for the database—which is done separately via cPanel). To access the backup utilities, we need to log in to cPanel, which is located at www.ourdomain.com/cpanel, and then enter our hosting account's username and password. In cPanel, we have the backup option on the main screen, as shown in the following screenshot: The Backups section has a number of options, but the one that we want is the Download a home directory Backup. This will generate a backup of all the files of our website and allow us to download it. In the previous screenshot, there is a warning message. This is because my web server does not have the option to back up the entire server, just an individualuser's webspace. The backup tool then takes a moment or two of processing, and then prompts us to download the backup file. Command-Line Backup To create a backup via the command line, we need to have SSH access to the server that is hosting our website. SSH is a protocol that allows us to remotely administer another machine using the command line. We can use a program such as Putty to connect to the server. We can download Putty from http://www.chiark.greenend.org.uk/~sgtatham/putty/. Putty only needs to be downloaded, after which it can be run straight away, and does not require to be installed. When we open the program, we are presented with a screen similar to the one shown in the following screenshot. We enter the server's address (i.e. the web address) into the Host Name box and then click on Open. Putty will then try to connect to the server, and will prompt us to enter our username and password, as shown in the following screenshot: Once we are connected, we can type two commands to back up our site. The first is to navigate to the folder that contains our TYPO3 installation. This depends entirely on the server's setup and your username, but is generally /home/the first 8 characters of your web address/public_html (you should contact your web host for more information or if you need help). Once we are in the correct folder, we can use the tar command to compress our TYPO3 folder to a single file named TYPO3. cd /home/michaelp/public_html/tar cvzf file.tar.gz typo3 Now that we have our backup created, we can download it from www.ourwebaddress.com/file.tar.gz (where we will be prompted to save the file). We should then delete this from our server once we have downloaded it.
Read more
  • 0
  • 0
  • 7616

article-image-bpmn-20-concepts-and-sales-quote-process
Packt
17 Sep 2010
6 min read
Save for later

BPMN 2.0 Concepts and The Sales Quote Process

Packt
17 Sep 2010
6 min read
(For more resources on Oracle, see here.) BPMN 2.0 concepts BPMN stands for Business Process Modeling Notation and is a public standard maintained by OMG. It describes a business-friendly, flow chart-like graphical notation that business process analysts and business users can use to model business processes and has support for process interactions, exception handling, compensation semantics, and so on. It is widely accepted by both commercial and open source BPMS tooling vendors. It is highly adaptable and can be used to capture everything from abstract process outlines to detailed process flows to implementation ready processes. One of the main value propositions of BPMN besides being a diagram standard is the precise semantics behind the diagram. The shape, the symbols (also referred to as markers), the borders, the placement of the BPMN diagram elements, as well as their properties have well defined meanings and have to be interpreted in the same manner by all tools. While BPMN 1.1 comprehensively addresses process modeling notations, it's failure to address an interchange format (for diagram exchange) has resulted in implementation vendors adopting different standards (BPEL, XPDL, other proprietary formats) to store BPMN process models leading to not only a loss of portability across tools but also making it difficult to communicate across the various stakeholders. The vision of BPMN is to have a single specification for notation, metamodel, and interchange. In addition, BPMN 2.0 has been expanded to include orchestrations and choreography of process models. Salient enhancements to BPMN 2.0 are as follows: BPMN 2.0 includes both diagram interchange as well as model interchange (the interchange formats can be either XML or UML) enabling portability of BPMN models across tool vendors. Formal execution semantics for all BPMN elements—BPMN 2.0 can not only be used to capture process models but can be used as an implementation model as well. IT simply layers the process execution details on top of the business process model leading to effective business-IT collaboration. Historically, business process models developed by business people have been technically separated from the process representations required by systems designed to implement and execute those processes. Thus, there was a need to manually translate the original business process models to the execution models. Vendors used standards such as BPEL and XPDL to save as well as execute BPMN Process models. Such translations are subject to errors and the impedance mismatch made it difficult for the process model and the executable process to be in sync with each other, as changes were made to both during the process development life cycle. With BPMN 2.0, there is no translation involved and the model is the implementation as well. Defines an extensibility mechanism for both Process model extensions and graphical extension. Refines event composition and correlation. Extends the definition of human interactions and aligns BPEL4People with the BPMN specification. Defines a Choreography model. A quick introduction to BPMN At its heart, BPMN has only three main elements, also referred to as Flow Objects—Activity (rectangle), Events (circle), and Gateways (diamond). An Activity represents some work done; Gateway represents a decision point or parallel forking or merge or join; Event represents either a trigger generated by the process or received by the process (from external source or from some other part of the process). These Flow Objects are linked by connections referred to as Sequential Flows. These Sequential Flows represent the chronological sequence of process steps. The preceding steps pass control to the following step(s) along the connection. The data is also passed along the connection flow. The Activity can be either a Task (an atomic process step) or Embedded Sub- process (compound process step). The Embedded Sub-Process can be either expanded or collapsed and has access to the process data. BPMN 2.0 supports different flavors of Tasks, namely: User Task for a human step managed by the workflow component of the BPM run-time engine; Manual Task for a human step that is not managed by the BPM run-time engine); Service Task for synchronous system interactions; Send Task and Receive Task for asynchronous system interactions; Script Task for scripting needs; Call Task for invoking another BPMN process (process chaining). The different task types have different symbols or markers to visually distinguish them. In BPMN, the lane objects are used to group activities based on the categories (can be human resources or system resources) that they are associated for better visualization purpose. In Oracle BPM Studio, the lanes are associated with the BPM Role object and the Performer of the User Task is automatically set to the BPM Role object associated with the lane. The User Task is associated with Process Participants or Performers who represent the business users who need to carry out the User Task. The associated Task (work to be performed) is shown in the inbox of the assigned Performers when the User Task is triggered. The actual work is performed only when the Performer executes on his Task. The Task is presented to the Performers through a browser-based worklist application. In BPM Studio the Process Participant or Performer is a BPM Role object in the Organization model. Oracle BPM Suite supports out-of-the-box workflow patterns. Workflow patterns allow users to declaratively specify approval chains, notifications, and escalation and expiration policies. This simplifies the process logic by encapsulating approval chains within reusable task components. It is always possible to model the approval pattern using simple Tasks, Gateways, and Events within the BPMN process—but for many processes it is more convenient to define workflow patterns as well as notification, expiration, and escalation policies as part of the user task definition. BPM Studio exposes these workflow patterns through six flavors of Interactive Tasks. The User Task refers to the Single Approver pattern and the participant or assignee is the member of the Role associated with the BPMN Process swim lane. The Management Task refers to the sequential management pattern and there are multiple participants assigned to the Task in a sequential pattern. Further, these participants are based on the Management hierarchy defined as part of LDAP and have the notion of a starting participant as well as the number of levels to be traversed up the management chain from the starting participant. The Group Task refers to the Parallel Voting pattern and the participants are members of the Role associated with the BPMN Process swim lane. The tasks are assigned in parallel to the participants in this case and the task is completed when a percentage of defined voting outcomes are reached. The FYI Task refers to the notification pattern and the participants are based on the Role associated with the BPMN Process swim lane. The task is completed as soon as the work items are assigned to the Task Inbox of the participants. Finally, the Complex Task for complex patterns involving task chaining and in this case the participants are not tied to the BPMN Process swim lanes.
Read more
  • 0
  • 0
  • 7615

article-image-calendaring-plone-3-education
Packt
31 Dec 2009
6 min read
Save for later

Calendaring with Plone 3 for Education

Packt
31 Dec 2009
6 min read
(For more resources on Plone, see here.) The techniques in this article are applicable in many organizational schemes. We present one common arrangement as an example: a top-level folder where visitors can browse the highest-profile events on the site. Show events on a calendar Plone's out-of-the-box Events folder provides a basic way to find events: it displays them in a chronological list broken into pages of 20 each. While fine for simple cases, this is cumbersome for visitors who want to look at events for a certain future date. Part of our goal is to set up a monthly calendar to make this common case easy: Our first step toward the monthly calendar is to replace Plone's stock Events folder. Out of the box, it holds individual events along with a summarizing collection that acts as its front page. If you don't need a drill-down way of browsing your events by subject—for example, if you have so few that they all fit comfortably on a calendar like the above—you can leave the default Events folder in place. Otherwise, follow the instructions below to replace it with a standalone collection; this will interact better with Plone's navigation portlet, which we will use to implement the drilldown browsing. If you choose to stick with the stock Events folder, you may still wish to adjust the criteria of its inner collection as described in the following: First, delete the Events folder. Replace it with a new collection called "Events". Give it an Item Type criterion, and tell it to pull in everything of the type Event. The original Events collection had a Start Date criterion to keep past events off the page. We won't need that, since the calendar view we are about to apply keeps past events tucked away implicitly—and we do want the ability to see old events when we navigate to previous months. The original collection also had a State criterion that limited it to showing published events. If you need only to hide unpublished events from visitors, then you can dispense with this: the internal search routines used by the collection will naturally show only what the current user has permission to see. Preventing a common contributor errorOften, a less proficient contributor will add an event, see it on his or her own calendar, and move on, neglecting to publish it. Adding the State criterion can serve as a reminder, preventing the event from appearing until properly published. The downside is that it makes the calendar useless for intranets: Events that aren't publicly visible will never show up. But if you have no non-public events to list, consider recreating the State criterion. If you never want to show access-controlled events on the main calendar add a State criterion limiting the collection to published items. The Events collection is finished. Next, we apply a monthly calendar view to our data. Meet Plone4Artists Calendar Plone's built-in event listings are rather basic. We need to do special tricks with collections and default views just to sort them in chronological order. At best, this is several extra clicks, and, at worst, it can confuse your less technical content contributors. A third-party product called Plone4Artists Calendar makes this easy and gives us several other capabilities to boot. Plone4Artists Calendar is the current frontrunner in the Plone calendaring space. It provides... Daily, weekly, monthly, and a few other calendar-like views for folders and collections Rudimentary recurring event support Hooks for pulling events from other systems in realtime, with a bit of coding Don't let the name "Plone4Artists" put you off if you aren't an artist; though the Plone4Artists suite of products came out of a project to provide artist community web sites, there's nothing particularly artist-centric about them. In fact, there has been discussion about renaming them. The runner up: CalendarXThe other Plone calendar worth considering is CalendarX, whose most recent release at the time of this writing is 0.9.1. 0.9.0 was the first update to the product since since 2005 and represents a major refit. Its ancient data-modeling internals were replaced with a modern Archetypes implementation, and one no longer needed to venture into the ZMI to do simple configuration. Speaking of configuration, CalendarX exposes a lot of it: 6 tabs packed with options for tweaking calendar format, event display, widgets, and more. There are 57 options alone regarding its CSS cosmetics. If you need a specific look and aren't comfortable writing your own CSS or template code, CalendarX may be the ticket.However, be warned that CalendarX has a history of being sporadically maintained. It lacked Plone 3 compatibility for a long time, and compatibility work began only when a group of stranded Plone 2.5 users at Pennsylvania State University put a week of development work toward it. Its internals still hold a lot of legacy that may prove difficult to maintain as Plone evolves.Plone4Artists Calendar, on the other hand, is a simpler product—both inside and outside and with all the good and bad that entails—and the winds of further community effort are blowing in its direction. Its maintainable design and the willingness of developers to work on it make it the solution least likely to leave you stranded; this is why it is our recommendation in this article. Install Plone4Artists Calendar Before we can make use of its views, we need to install the product—actually, two products and several supporting packages. Weave these directives into the proper sections of your buildout.cfg... [buildout]...other directives...eggs = ...other eggs here... p4a.plonecalendar p4a.ploneevent[instance]...other directives...zcml = ...other ZCML listings here... # Plone4Artists Calendar doesn't support Plone 3.3's auto-ZCMLloading as of 2.0a2. p4a.plonecalendar-meta p4a.plonecalendar p4a.ploneevent[productdistros]recipe = plone.recipe.distrosurls =http://plone.org/products/calendaring/releases/0.4/calendaring-0-4-0.tgz ...run buildout, restart Zope/Plone, and install the Plone4ArtistsCalendar (p4a.plonecalendar) and Recurring Events (p4a.ploneevent) products in Site Setup → Add-on Products. Now that everything is installed, we apply a monthly calendar view to the Events collection: Navigate back to the Events collection, and choose Calendar from the new Sub-types menu. If it isn't already selected, choose Month view from the Display menu. Presto—we have a monthly calendar showing every event on our site.
Read more
  • 0
  • 0
  • 7614

article-image-testing-your-jboss-drools-business-rules-using-unit-testing
Packt
14 Oct 2009
5 min read
Save for later

Testing your JBoss Drools Business Rules using Unit Testing

Packt
14 Oct 2009
5 min read
What is unit testing? A good enterprise computer system should be built as if it was made of Lego bricks. Your rules are only a piece of the puzzle. You'll need to go back to the Lego box to get pieces that talk to the database, make web pages, talk to other systems that you may have in your company (or organization), and so on. Just as Lego bricks can be taken apart and put together in many different ways, the components in a well-designed system should be reusable in many different systems. Before you use any of these components (or 'bricks') in your system, you will want to be sure that they work. For Lego bricks this is easy—you can just make sure that none of the studs are broken. For components this is a bit harder—often, you can neither see them, nor do you have any idea whether their inputs and outputs are correct. Unit testing makes sure that all of the component pieces of your application work, before you even assemble them. You can unit test manually, but just like FIT requirements testing, you're going to 'forget' to do it sooner or later. Fortunately, there is a tool to automate your unit tests known as Junit (for Java; there are also versions for many other languages, such as .Net). Like Drools and FIT, Junit is open source. Therefore, we can use it on our project without much difficulty. Junit is integrated into the JBoss IDE and is also pretty much an industry standard, so it's easy to find more information on it. A good starting point is the project's home page at http://www.Junit.org. The following points can help you to decide when to use unit testing, and when to use the other forms of testing that we talked about: If you're most comfortable using Guvnor, then use the test scenarios within Guvnor. As you'll see shortly, they're very close to unit tests. If the majority of your work involves detailing and signing off against the requirement documents, then you should consider using FIT for Rules. If you're most comfortable using Java, or some other programming language, then you're probably using (J)unit tests already—and we can apply these unit tests to rule testing. In reality, your testing is likely to be a mix of two or three of these options. Why unit test? An important point to note is that you've already carried out unit testing in the rules that we wrote earlier. OK, it was manual unit testing, but we still checked that our block of rules produced the outcome that we expected. All we're talking about here is automating the process. Unit testing also has the advantage of documenting the code because it gives a working example of how to call the rules. It also makes your rules and code more reusable. You've just proved (in your unit test) that you can call your code on a standalone basis, which is an important first step for somebody else to be able to use it again in the future. You do want your rules to be reused, don't you? Unit testing the Chocolate Shipments sample As luck would have it, our Chocolate Shipments example also contains a unit test. This is called DroolsUnitTest.java, and it can be found in the test/java/net/firstpartners/chap7 folder. Running the Junit test is similar to running the samples. In the JBoss IDE Navigator or package explorer, we select DroolsUnitTest.java, right-click on it, and then select Run as | Junit test from the shortcut menu. All being well, you should see some messages appear on the console. We're going to ignore the console messages; after all, we're meant to be automating our testing, not manually reading the console. The really interesting bit should appear in the IDE— the Junit test result, similar to the screenshot shown below. If everything is OK, we should see the green bar displayed—success! We've run only one unit test, so the output is fairly simple. From top to bottom we have: the time it took to run the test; the number of errors and failures (both zero—we'll explain the difference shortly, but having none of them is a good thing), the green bar (success!), and a summary of the unit tests that we've just run (DroolsUnitTest). If you were running this test prior to deploying to production, all you need to know is that the green bar means that everything is working as intended. It's a lot easier than inspecting the code line by line. However, as this is the first time that we're using a unit test, we're going to step through the tests line by line. A lot of our Junit test is similar to MultipleRulesExample.java. For example, the unit test uses the same RuleRunner file to load and call the rules. In addition, the Junit test also has some automated checks (asserts) that give us the green bar when they pass, which we saw in the previous screenshot.
Read more
  • 0
  • 0
  • 7608
article-image-building-crud-application-zk-framework
Packt
21 Oct 2009
5 min read
Save for later

Building a CRUD Application with the ZK Framework

Packt
21 Oct 2009
5 min read
An Online Media Library There are some traditional applications that could be used to introduce a framework. One condition for the selection is that the application should be a CRUD (Create —Read—Update—Delete) application. Therefore, an 'Online Media Library', which has all four operations, would be appropriate. We start with the description of requirements, which is the beginning of most IT projects. The application will have the following features: Add new media Update existing media Delete media Search for the media (and show the results) User roles (administrator for maintaining the media and user accounts for browsing the media) In the first implementation round the application should only have some basic functionality that will be extended step by step. A media item should have the following attributes: A title A type (Song or Movie) An ID which could be defined by the user A description An image The most important thing at the start of a project is to name it. We will call our project ZK-Medialib. Setting up Eclipse to Develop with ZK We use version 3.3 of Eclipse, which is also known as Europa release. You can download the IDE from http://www.eclipse.org/downloads/. We recommend using the version "Eclipse IDE for Java EE Developers". First we have to make a file association for the .zul files. For that open the Preferences dialog with Window | Preferences. After that do the following steps: Type Content Types into the search dialog. Select Content Types in the tree. Select XML in the tree. Click Add and type *.zul. See the result. The steps are illustrated in the picture below: With these steps, we have syntax highlighting of our files. However, to have content assist, we have to take care about the creation of new files. The easiest way is to set up Eclipse to work with zul.xsd. For that open the Preferences dialog with Window | Preferences. After that do the following steps: Type XML Catalog into the search dialog. Select XML Catalog in the tree. Press Add and fill out the dialog (see the second dialog below). See the result. Now we can easily create new ZUL files with the following steps: File | New | Other, and select XML: Type in the name of the file (for example hello.zul). Press Next. Choose Create XML file from an XML schema file: Press Next. Select Select XML Catalog entry. Now select zul.xsd: Now select the Root Element of the page (e.g. window). Select Finish. Now you have a new ZUL file with content assist. Go into the generated attribute element and press Alt+Space. Setting up a New Project The first thing we will need for the project is the framework itself. You can download the ZK framework from http://www.zkoss.org. At the time of writing, the latest version of ZK is 2.3.0. After downloading and unzipping the ZK framework we should define a project structure. A good structure for the project is the directory layout from the Maven project (http://maven.apache.org/). The structure is shown in the figure below. The directory lib contains the libraries of the ZK framework. For the first time it's wise to copy all JAR files from the ZK framework distribution. If you unzip the distribution of the version 2.3.0 the structure should look like the figure below. The structure below shows the structure of the ZK distribution. Here you can get the files you need for your own application. For our example, you should copy all JAR files from lib, ext, and zkforge to the WEB-INF/lib directory of your application. It's important that the libraries from ext and zkforge are copied direct to WEB-INF/lib. Additionally copy the directories tld and xsd to the WEB-INF directory of your application. Now after the copy process, we have to create the deployment descriptor (web.xml) for the web application. Here you can use web.xml from the demo application, which is provided from the ZK framework. For our first steps, we need no zk.xml (that configuration file is optional in a ZK application). The application itself must be run inside a JEE (Java Enterprise Edition) Webcontainer. For our example, we used the Tomcat container from the Apache project (http://tomcat.apache.org). However, you can run the application in each JEE container that follows the Java Servlet Specification 2.4 (or higher) and runs under a Java Virtual Machine 1.4 (or higher). We create the zk-media.xml file for Tomcat, which is placed in conf/Catalina/localhost of the Tomcat directory. <Context path="/zk-media" docBase="D:/Development/workspaces/workspace-zk-medialib/ZK-Medialib/src/main/webapp" debug="0"privileged="true" reloadable="true" crossContext="false"><Logger className="org.apache.catalina.logger.FileLogger"directory="D:/Development/workspaces/workspace-zk-medialib/logs/ZK-Medialib" prefix="zkmedia-" suffix=".txt" timestamp="true"/></Context> With the help of this context file, we can directly see the changes of our development, since, we set the root of the web application to the development directory.  
Read more
  • 0
  • 0
  • 7568

article-image-transferring-data-ms-access-2003-sql-server-2008
Packt
07 Oct 2009
3 min read
Save for later

Transferring Data from MS Access 2003 to SQL Server 2008

Packt
07 Oct 2009
3 min read
Launching the Import and Export Wizard Click Start | All Programs| SQL Server 2008 and click on Import and Export Data (32 bit) as shown in the next figure. This brings up the - "Welcome to the SQL Server Import and Export Wizard”. Make sure you read the explanation on this window. Connecting to Source and Destination databases You will connect to the source and destination. In this example the data to be transferred is in a MS Access 2003 database. A table in the Northwind.mdb file available in MS Access will be used for the source. The destination is the 'tempdb' database (or the master database). These are chosen for no particular reason and you can even create a new database on the server at this point. Connecting to the Source Click Next. The default window gets displayed as shown. The data source is SQL Server Native Client 10.0. As we are transferring from MS Access 2003 to SQL Server 2008 we need to use an appropriate data source. Click on the drop-down handle for the data source and choose Microsoft Access as shown in the next figure. The 'SQL Server Import and Export Wizard' changes to the following. Click on the Browse... button to locate the Northwind.mdb file on your machine also as shown. For Username use the default 'Admin'. Click the Advanced button. It opens the Data Link properties window as shown. You can test the connection as well as look at other connection related properties using the Connection and Advanced tabs. Click OK to the Microsoft Data Link message as well as to the Data Link Properties window. Click Next. Choose Destination The Choose a Destination page of the wizard gets displayed. The Destination source (SQL Server Native Client 10.0) and the Server name that automatically shows up are defaults. These may be different in your case. Accept them. The authentication information is also correct (the SQL Server is configured for Windows Authentication). Click on the drop-down handle for the Database presently displaying <default> as shown. Choose tempdb and click on the Next button. Choosing a table or query for the data to be transferred You can transfer data in table or tables; view or views by copying them over, or you may create a query to collect the data you want transferred. In this article both options will described. Copying a table over to destination The Specify Table Copy or Query page of the wizard gets displayed as shown. First we will copy data from a table. Later we will see how to write a query to do data transfer. Click Next. The Select Source Tables and Views page gets displayed as shown. When you place a check mark for say, Customers, the Destination column gets an entry for the selection you made. We assume that just this table is transferred. However you can see that you can transfer all of them in one shot. This window also helps you with two more important things. First you can Preview... the data you are transferring. Secondly, you can edit the mappings, the scheme of how source columns go over to destination.
Read more
  • 0
  • 0
  • 7507

article-image-navigation-stack-robot-setups
Packt
10 Oct 2013
7 min read
Save for later

Navigation Stack - Robot Setups

Packt
10 Oct 2013
7 min read
The navigation stack in ROS In order to understand the navigation stack, you should think of it as a set of algorithms that use the sensors of the robot and the odometry, and you can control the robot using a standard message. It can move your robot without problems (for example, without crashing or getting stuck in some location, or getting lost) to another position. You would assume that this stack can be easily used with any robot. This is almost true, but it is necessary to tune some configuration files and write some nodes to use the stack. The robot must satisfy some requirements before it uses the navigation stack: The navigation stack can only handle a differential drive and holonomic-wheeled robots. The shape of the robot must be either a square or a rectangle. However, it can also do certain things with biped robots, such as robot localization, as long as the robot does not move sideways. It requires that the robot publishes information about the relationships between all the joints and sensors' position. The robot must send messages with linear and angular velocities. A planar laser must be on the robot to create the map and localization. Alternatively, you can generate something equivalent to several lasers or a sonar, or you can project the values to the ground if they are mounted in another place on the robot. The following diagram shows you how the navigation stacks are organized. You can see three groups of boxes with colors (gray and white) and dotted lines. The plain white boxes indicate those stacks that are provided by ROS, and they have all the nodes to make your robot really autonomous: In the following sections, we will see how to create the parts marked in gray in the diagram. These parts depend on the platform used; this means that it is necessary to write code to adapt the platform to be used in ROS and to be used by the navigation stack. Creating transforms The navigation stack needs to know the position of the sensors, wheels, and joints. To do that, we use the TF (which stands for Transform Frames) software library. It manages a transform tree. You could do this with mathematics, but if you have a lot of frames to calculate, it will be a bit complicated and messy. Thanks to TF, we can add more sensors and parts to the robot, and the TF will handle all the relations for us. If we put the laser 10 cm backwards and 20 cm above with regard to the origin of the coordinates of base_link, we would need to add a new frame to the transformation tree with these offsets. Once inserted and created, we could easily know the position of the laser with regard to the base_link value or the wheels. The only thing we need to do is call the TF library and get the transformation. Creating a broadcaster Let's test it with a simple code. Create a new file in chapter7_tutorials/src with the name tf_broadcaster.cpp, and put the following code inside it: #include <ros/ros.h> #include <tf/transform_broadcaster.h> int main(int argc, char** argv){ ros::init(argc, argv, "robot_tf_publisher"); ros::NodeHandle n; ros::Rate r(100); tf::TransformBroadcaster broadcaster; while(n.ok()){ broadcaster.sendTransform( tf::StampedTransform( tf::Transform(tf::Quaternion(0, 0, 0, 1), tf::Vector3(0.1, 0.0, 0.2)), ros::Time::now(),"base_link", "base_laser")); r.sleep(); } } Remember to add the following line in your CMakelist.txt file to create the new executable: rosbuild_add_executable(tf_broadcaster src/tf_broadcaster.cpp) And we also create another node that will use the transform, and it will give us the position of a point of a sensor with regard to the center of base_link (our robot). Creating a listener Create a new file in chapter7_tutorials/src with the name tf_listener.cpp and input the following code: #include <ros/ros.h> #include <geometry_msgs/PointStamped.h> #include <tf/transform_listener.h> void transformPoint(const tf::TransformListener& listener){ //we'll create a point in the base_laser frame that we'd like to transform to the base_link frame geometry_msgs::PointStamped laser_point; laser_point.header.frame_id = "base_laser"; //we'll just use the most recent transform available for our simple example laser_point.header.stamp = ros::Time(); //just an arbitrary point in space laser_point.point.x = 1.0; laser_point.point.y = 2.0; laser_point.point.z = 0.0; geometry_msgs::PointStamped base_point; listener.transformPoint("base_link", laser_point, base_point); ROS_INFO("base_laser: (%.2f, %.2f. %.2f) -----> base_link: (%.2f, %.2f, %.2f) at time %.2f", laser_point.point.x, laser_point.point.y, laser_point.point.z, base_point.point.x, base_point.point.y, base_point.point.z, base_point.header.stamp.toSec()); ROS_ERROR("Received an exception trying to transform a point from "base_laser" to "base_link": %s", ex.what()); } int main(int argc, char** argv){ ros::init(argc, argv, "robot_tf_listener"); ros::NodeHandle n; tf::TransformListener listener(ros::Duration(10)); //we'll transform a point once every second ros::Timer timer = n.createTimer(ros::Duration(1.0), boost::bind(&transformPoint, boost::ref(listener))); ros::spin(); } Remember to add the line in the CMakeList.txt file to create the executable. Compile the package and run both the nodes using the following commands: $ rosmake chapter7_tutorials $ rosrun chapter7_tutorials tf_broadcaster $ rosrun chapter7_tutorials tf_listener Then you will see the following message: [ INFO] [1368521854.336910465]: base_laser: (1.00, 2.00. 0.00) -----> base_link: (1.10, 2.00, 0.20) at time 1368521854.33 [ INFO] [1368521855.336347545]: base_laser: (1.00, 2.00. 0.00) -----> base_link: (1.10, 2.00, 0.20) at time 1368521855.33 This means that the point that you published on the node, with the position (1.00, 2.00, 0.00) relative to base_laser, has the position (1.10, 2.00, 0.20) relative to base_link. As you can see, the tf library performs all the mathematics for you to get the coordinates of a point or the position of a joint relative to another point. A transform tree defines offsets in terms of both translation and rotation between different coordinate frames. Let us see an example to help you understand this. We are going to add another laser, say, on the back of the robot (base_link): The system had to know the position of the new laser to detect collisions, such as the one between wheels and walls. With the TF tree, this is very simple to do and maintain and is also scalable. Thanks to tf, we can add more sensors and parts, and the tf library will handle all the relations for us. All the sensors and joints must be correctly configured on tf to permit the navigation stack to move the robot without problems, and to exactly know where each one of their components is. Before starting to write the code to configure each component, keep in mind that you have the geometry of the robot specified in the URDF file. So, for this reason, it is not necessary to configure the robot again. Perhaps you do not know it, but you have been using the robot_state_publisher package to publish the transform tree of your robot. We used it for the first time; therefore, you do have the robot configured to be used with the navigation stack. Watching the transformation tree If you want to see the transformation tree of your robot, use the following command: $ roslaunch chapter7_tutorials gazebo_map_robot.launch model:= "`rospack find chapter7 _tutorials`/urdf/robot1_base_04.xacro" $ rosrun tf view_frames The resultant frame is depicted as follows: And now, if you run tf_broadcaster and run the rosrun tf view_frames command again, you will see the frame that you have created by code: $ rosrun chapter7_tutorials tf_broadcaster $ rosrun tf view_frames The resultant frame is depicted as follows:
Read more
  • 0
  • 0
  • 7505
article-image-tips-and-tricks-working-jquery-and-wordpress
Packt
23 Sep 2010
10 min read
Save for later

Tips and Tricks for Working with jQuery and WordPress

Packt
23 Sep 2010
10 min read
  WordPress 3.0 jQuery Enhance your WordPress website with the captivating effects of jQuery. Enhance the usability and increase visual interest in your WordPress 3.0 site with easy-to-implement jQuery techniques Create advanced animations, use the UI plugin to your advantage within WordPress, and create custom jQuery plugins for your site Turn your jQuery plugins into WordPress plugins and share with the world Implement all of the above jQuery enhancements without ever having to make a WordPress content editor switch over into HTML view   Read more about this book (For more resources on WordPress and jQuery, see here.) The following are the tips and tricks required for working with jQuery and WordPress. Keep a code arsenal A "snippet collection" or, what I call my "code arsenal" will go a long way to help you out, not just with jQuery and WordPress code, but also with the general HTML markup and even CSS solutions you create, not to mention any other code language you work in. I'm terrible at remembering syntax for code, markup, and CSS. I often know what I need, but can never quite recall exactly how it's supposed to be typed. I used to spend hours going through various stylesheets, markup, and codes from previous projects to copy into my current project as well as googling (and "re-googling") web pages that had samples of the syntax I needed. If you often find yourself in a similar situation, using the Snippets or Clip features that are usually available in good HTML/Code editors will free you from this mundane (and very time consuming) task. You simply type or paste the WordPress template tags, functions, PHP code, key CSS rules, and jQuery functions (and any other code syntax, whatever you find you need to use the most), into the Snippets or Clips panel available in your editor, and the application saves it for you, for future use. As you work on different projects, you'll come up with solutions that you'll probably want to use again in the future, say, a set of CSS rules for unordered lists that make a nice gallery view, or a very clever use of two jQuery functions together. Every time you create something you think may come in handy (and a lot of it will come in handy again), be sure to save it right then and there, for future reference. Good editors such as Dreamweaver, HTML-Kit, and Coda usually have the ability to organize snippets and keep them logically grouped so they're easy to access. Some editors will even let you assign custom "key shortcuts" and/or drag-and-drop to your clips right into your working file. How easy is that? Free your arsenal Once you discover how handy this is, you might want to have your arsenal available to other programs you work with, especially if you switch between multiple editors and authoring environments. I suggest you invest in a multi- paste/clip board application that lets you save and organize your code snippets. When I was on a PC, I used a great little app called Yankee Clipper 3 (which is free and is available at http://www.intelexual.com/products/YC3/), and now on the Mac, I use iPaste (which has a modest price; go to http://www.iggsoftware.com/ipaste/). In addition to having your arsenal handy from any application, being able to go back through the last 10 or so items you copied to the clip board is a real time saver when you're working on a project. Your arsenal on-the-go Last, I find I like to take most of my arsenal with me. If you use a handheld device or have a phone with a note app that lets you categorize and search for notes (especially the one that will let you sync from your desktop or a web service), you'll probably find it useful to keep some or all of your arsenal in it so you can easily look up syntax from your arsenal at any time. I occasionally freelance at places that require me to use one of their computers and not my laptop, so having access to my arsenal on my device is very useful. Palm's native note app suited me great in this capacity for years and years; I now keep a large part of my arsenal in Google docs and use a little desktop app called NoteSync, which lets you write and view Google docs notes quickly (they'll have an Android app out soon, but in the meantime I use Gdocs on my Android device to see my notes). I have many friends who swear by EverNote's system (though, their mobile app only works offline on the iPhone and not on Android—as of yet). Once all your often used and creative one-off solutions are all located in a convenient (hopefully categorized and key-word-searchable) place, you'll be amazed at the amount of speed your development picks up and how much more relaxing it is. jQuery tips and tricks for working in WordPress Let's start-off with some of my favorite jQuery tips and tricks, before focusing on WordPress.There are also a few nuggets that will help you speed up your jQuery development. Try to use the latest version of jQuery This is one of the drawbacks to using the bundled WordPress version: it may get a little behind the current version of jQuery until the next version of WordPress comes out. I'm all for staying on top of the current version as jQuery's top goals for version releases are not just to provide new functionality, but continually streamline and improve the performance and speed of the existing functionality. If the latest version of jQuery available on CDN is greater than the version that's bundled, be sure to deregister jQuery first or restrict your newer version with the if else statements, so it loads on the front end of the site on required pages only. Otherwise, you may create problems with plugins using the bundled version of WordPress. Stay current with the Google CDN The best way to stay current is to simply use Google's CDN.There are additional advantages to loading up from Google's CDN instead for your project's hosted server. Instead of having to load JavaScript's, libraries and assets one by one from your server, your site can simultaneously load the main library from the Google CDN in addition to other local jQuery scripts and collateral. The bonus is that jQuery will be cached for users who've visited other sites that load it up from Google's CDN. Stay in No Conflict mode The great thing about WordPress is that a site can have so many people contributing to it in lots of different ways: writing content, working on the theme, and adding WordPress plugins. One of the worst things about WordPress is that so many people can easily contribute who knows what to a site, depending on their admin status, some other collaborator could add to them, or what plugins they could install. Staying in No Conflict mode is a must for WordPress. This in conjunction with using the wp_enque_script to load in WordPress will ensure that jQuery doesn't get "pushed out" if anyone loads up any other plugin that uses say MooTools or Scriptaculous, or even just an older version of jQuery. It's easy to stay in noConflict mode. Just use jQuery instead of the shortcut dollar sign ($) in front of your scripts. jQuery('.selector').function(); Make sure other scripts in the theme or plugin use the Script API If you're using a theme or a plugin from a third party, take a look through the theme's header.php file or the plugin's PHP pages and double-check that all scripts have been loaded in using the register and wp_enqueu_script methods. I've had a few instances that were rather frustrating and caused some hair-pulling, as we tried to figure out why my jQuery scripts were not working or wondering how I "broke" them porting them over to the live site. Turns out, the live site had a plugin installed that my sandbox site didn't, and you guessed it, that plugin was including an older version of jQuery and a custom script file using hard-coded script tags instead of the wp_enqueue_script method. Once this was figured out and straightened up, setting everything into noConflict mode, everything worked fine again! Check your jQuery syntax This one always gets me. You write up a nifty little jQuery chain, add a few tweaks to it, and the darn thing just stops working. And you know it's right! Well, at least, you think it's right. Right? This is where a great code editor comes in handy. You'll want some nice find features that let you step through and look at each returned find, as well as let you run a find not just on the whole document, but on individual selections. I like to select the just the "offending chain" and run the following find features on it to see what comes up. Colons and semicolons Do a find for : (colons); you'll probably find a few that are accidentally set up as ; (semicolons) in your function's various object parameters, or you may have typed a colon where a semicolon should have been there. Closing parenthesis I'll also run a find on closing parenthesis, ), and make sure each one that comes up is part of a continuing chain or the end of the chain marked with a;. Mismatched double and single quotes Last, a quick check for matched-up single and double quotes sometimes shows me where I've messed up. Panic's Coda lets you place in "wild cards" into the find so a search for "*' or '*" usually turns up a pesky problem. Most good code editors have color-coded syntax, which really helps in recognizing when something isn't right with your syntax, such as not having a closing quote mark at all or parenthesis. But, the issues above are tricky as they'll still often display as proper color coded syntax, so you don't know until you run your script that something's wrong. Use Firefox and Firebug to help with debugging Firebug has a feature called "console logging". This is one of many great features of Firebug in my opinion. For years I often resorted to using JavaScript's "alert" statement to try and show me what was going on "inside" my work but the Firebug console handles so much more than that. This is really useful because sometimes you have to debug a "live" site and setting up JavaScript alerts is a little risky as you may confuse visitors to the site. Using Firebug's console logging eliminates that. First up, there's the console.log and console.info statements which you can add to your jQuery scripts to pass info to and have a plethora of useful (and sometimes not-so-useful, but interesting) information about your script returned. console.profile and console.time are great for measuring how fast you scripts are being processed by the browser. For a complete overview of everything Firebug's console can do, check out: http://www.getfirebug.com/logging.
Read more
  • 0
  • 0
  • 7455

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