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

How-To Tutorials

7019 Articles
article-image-understanding-business-activity-monitoring-oracle-soa-suite
Packt
28 Oct 2009
14 min read
Save for later

Understanding Business Activity Monitoring in Oracle SOA Suite

Packt
28 Oct 2009
14 min read
How BAM differs from traditional business intelligence The Oracle SOA Suite stores the state of all processes in a database in documented schemas so why do we need yet another reporting tool to provide insight into our processes and services? In other words how does BAM differ from traditional BI (Business Intelligence)? In traditional BI, reports are generated and delivered either on a scheduled basis or in response to a user request. Any changes to the information will not be reflected until the next scheduled run or until a user requests the report to be rerun. BAM is an event-driven reporting tool that generates alerts and reports in real time, based on a continuously changing data stream, some of whose data may not be in the database. As events occur in the Services and Processes, the business has defined they are captured by BAM and reports and views are updated in real time. Where necessary these updated reports are delivered to users. This delivery to users can take several forms. The best known is the dashboard on users' desktops that will automatically update without any need for the user to refresh the screen. There are also other means to deliver reports to the end user, including sending them via a text message or an email. Traditional reporting tools such as Oracle Reports and Oracle Discoverer as well as Oracles latest Business Intelligence Suite can be used to provide some real-time reporting needs but they do not provide the event driven reporting that gives the business a continuously updating view of the current business situation. Event Driven Architecture Event Driven Architecture (EDA) is about building business solutions around responsiveness to events. Events may be simple triggers such as a stock out event or they may be more complex triggers such as the calculations to realize that a stock out will occur in three days. An Event Driven Architecture will often take a number of simple events and then combine them through a complex event processing sequence to generate complex events that could not have been raised without aggregation of several simpler events. Oracle BAM scenarios Oracle Business Activity Monitoring is typically used to monitor two distinct types of real-time data. Firstly it may be used to monitor the overall state of processes in the business. For example it may be used to track how many auctions are currently running, how many have bids on them, and how many have completed in the last 24 hours (or other time periods). Secondly it may be used to track in real-time Key Performance Indicators or KPIS. For example it may be used to provide a real-time updating dashboard to a seller to show the current total value of all the sellers' auctions and to track this against an expected target. In the first case, we are interested in how business processes are progressing and are using BAM to identify bottlenecks and failure points within those processes. Bottlenecks can be identified by too much time being spent on given steps in the process. BAM allows us to compute the time taken between two points in a process, such as the time between order placement and shipping, and provide real-time feedback on those times. Similarly BAM can be used to track the percentage drop-out rate between steps in a sales process, allowing the business to take appropriate action. In the second case, our interest is on some aggregate number, such as our total liabilities should we win all the auctions we are bidding on. This requires us to aggregate results from many events, possibly performing some kind of calculation on them to provide us with a single KPI that gives an indication to the business of how things are going. BAM allows us to continuously update this number in real on a dashboard without the need for continued polling. It also allows us to trigger alerts, perhaps through email or SMS, to notify an individual, when a threshold is breached. In both cases reports delivered can be customized based on the individual receiving the report. BAM architecture It may seem odd to have a section on architecture in the middle of a article about how to effectively use BAM, but key to successful utilization of BAM is an understanding of how the different tiers relate to each other. Logical view The following diagram represents a logical view of how BAM operates. Events are acquired from one or more sources through event acquisition and then normalized, correlated, and stored in event storage (generally a memory area in BAM that is backed up to disc). The report cache generates reports based on events in storage and then delivers those reports, together with real-time updates through the report delivery layer. Event processing is also performed on events in storage, and when defined conditions are met, alerts will be delivered through the alert delivery service. Physical view To better understand the physical view of the architecture of BAM, we have divided this section into four parts. Let us discuss these in detail. Capture This logical view maps onto the physical BAM components shown in the following diagram. Data acquisition in the SOA Suite is handled by sensors in BPEL and ESB. BAM can also receive events from JMS message queues and access data in databases (useful for historical comparison). For complex data formats or for other data sources then Oracle Data Integrator (ODI is a separate product to the SOA Suite) is recommended by Oracle. Although potentially less efficient and more work than running ODI, it is also possible to use adapters to acquire data from multiple sources and feed it into BAM through ESB or BPEL. At the data capture level we need to think of the data items that we can provide to feed the reports and alerts that we desire to generate. We must consider the sources of that data and the best way to load it into BAM. Store Once the data is captured, it is then stored in a normalized form in the Active Data Cache (ADC). This storage facility has the ability to do simple correlation based on fields within the data, and multiple data items received from the acquisition layer may update just a single object in the data cache. For example the state of a given BPEL process instance may be represented by a single object in the ADC and all updates to that process state will just update that single data item rather than creating multiple data items. Process Reports are run based on user demand. Once a report is run it will update the user's screen on a real time basis. Where multiple users are accessing the same report only one instance of the report is maintained by the report server. As events are captured and stored in real time the report engine will continuously monitor them for any changes that need to be made to those reports which are currently active. When changes are detected that impact active reports, then the appropriate report will be updated in memory and the updates sent to the user screen. In addition to the event processing required to correctly insert and update items in the ADC, there is also a requirement to monitor items in the ADC for events that require some sort of action to be taken. This is the job of the event processor. This will monitor data in the ADC to see if registered thresholds on values have been exceeded or if certain time-outs have expired. The event processor will often need to perform calculations across multiple data items to do this. Deliver Delivery of reports takes place in two ways. First, users request reports to be delivered to their desktop by selecting views within BAM. These reports are delivered as HTML pages within a browser and are updated whenever the underlying data used in the report changes. The second approach is that reports are sent out as a result of events being triggered by the Event Processing Engine. In the latter case, the report may be delivered by email, SMS, or voice messaging using the notifications service. A final option available for these event generated reports is to invoke a web service to take some sort of automated action. Closing the loop While monitoring what is happening is all very laudable, it is only of benefit if we actually do something about what we are monitoring. BAM provides the real-time monitoring ability very well but it also provides the facility to invoke other services to respond to undesirable events such as stock outs. The ability to invoke external services is crucial to the concept of a closed loop control environment where as a result of monitoring we are able to reach back into the processes and either alter their execution or start new ones. For example when a stock out or low stock event is raised then the message centre could invoke a web service requesting a supplier to send more stock to replenish inventory. Placing this kind of feedback mechanism in BAM allows us to trigger events across multiple applications and locations in a way that may not be possible within a single application or process. For example, in response to a stock out, instead of requesting our supplier to provide more stock, we may be monitoring stock levels in independent systems and, based on stock levels elsewhere, may redirect stock from one location to another. BAM platform anomaly In 10g SOA Suite, BAM runs only as a Windows application. Unlike the rest of SOA Suite, it does not run on a JEE Application Server and it can only run on the Windows platform. In the next release, 11g, BAM will be provided as a JEE application that can run on a number of application servers and operating systems. User interface Development in Oracle BAM is done through a web-based user interface. This user interface gives access to four different applications that allow you to interact with different parts of BAM. These are: Active Viewer for giving access to reports; this relates to the deliver stage for user requested reports. Active Studio for building reports; this relates to the 'process' stage for creating reports. Architect for setting up both inbound and outbound events. Data elements are defined here as data sources. Alerts are also configured here. This covers setting up, acquire and store stages as well as the deliver stage for alerts. Administrator for managing users and roles as well as defining the types of message sources. We will not examine the applications individually but will take a task-focused look at how to use them as part of providing some specific reports. Monitoring process state Now that we have examined how BAM is constructed, let us use this knowledge to construct some simple dashboards that track the state of a business process. We will instrument a simple version of an auction process. The process is shown in the following figure: An auction is started and then bids are placed until the time runs out at which point the auction is completed. This is modelled in BPEL. This process has three distinct states: Started Bid received Completed We are interested in the number of auctions in each state as well as the total value of auctions in progress. One needs to follow these steps to build the dashboard: Define our data within the Active Data Cache Create sensors in BPEL and map to data in the ADC Create suitable reports Run the reports Defining data objects Data in BAM is stored in data objects. Individual data objects contain the information that is reported in BAM dashboards and may be updated by multiple events. Generally BAM will report against aggregations of objects, but there is also the ability for reports to drill down into individual data objects. Before defining our data objects let's group them into an Auction folder so they are easy to find. To do this we use the BAM Architect application and select Data Objects which gives us the following screen: We select Create subfolder to create the folder and give it a name Auction. We then select Create folder to actually create the folder and we get a confirmation message to tell us that the folder was created. Notice that once created, the folder also appears in the Folders window on the left-hand side of the screen. Now we have our folder we can create a data object. Again we select Data Objects from the drop-down menu. To define the data objects that are to be stored in our Active Data Cache, we open the Auction folder if it is not already open and selectCreate Data Object. If we don't select the Auction folder then we pick it later when filling in the details of the data object. We need to give our object a unique name within the folder and optionally provide it with a tip text that helps explain what the object does when the mouse is moved over it in object listings. Having named our object we can now create the data fields by selecting Add a field. When adding fields we need to provide a name and type as well as indicating if they must contain data; the default Nullable does not require a field to be populated. We may also optionally indicate if a field should be public "available for display" and what if any tool tip text it should have. Once all the data fields have been defined then we can click Create Data Object to actually create the object as we have defined it. We are then presented with a confirmation screen that the object has been created. Grouping data into hierarchies When creating a data object it is possible to specify Dimensions for the object. A dimension is based on one or more fields within the object. A given field can only participate in one dimension. This gives the ability to group the object by the fields in the given dimension. If multiple fields are selected for a single dimension then they can be layered into a hierarchy, for example to allow analysis by country, region, and city. In this case all three elements would be selected into a single dimension, perhaps called geography. Within geography a hierarchy could be set up with country at the top, region next, and finally city at the bottom, allowing drill down to occur in views. Just as a data object can have multiple dimensions, a dimension can also have multiple hierarchies. A digression on populating data object fields In the previous discussion, we mentioned the Nullable attribute that can be attached to fields. This is very important as we do not expect to populate all or even most of the fields in a data object at one moment in time. Do not confuse data objects with the low level events that are used to populate them. Data objects in BAM do not have a one-to-one correspondence with the low level events that populate them. In our auction example there will be just one auction object for every auction. However there will be at least two and usually more messages for every auction; one message for the auction starting, another for the auction completing, and additional messages for each bid received. These messages will all populate or in some cases overwrite different parts of the auction data object. The table shows how the three messages populate different parts of the data object. Message Auction ID State Highest bid Reserve Expires Seller Highest bidder Auction Started Inserted Inserted Inserted Inserted Inserted Inserted   Bid Received   Updated Updated       Updated Auction Finished   Updated           Summary In this article we have explored how Business Activity Monitoring differs from and is complementary to more traditional Business Intelligence solutions such as Oracle Reports and Business Objects. We have explored how BAM can allow the business to monitor the state of business targets and Key Performance Indicators, such as the current most popular products in a retail environment or the current time taken to serve customers in a service environment.
Read more
  • 0
  • 0
  • 7172

article-image-creating-joomla-15-templates
Packt
27 Oct 2009
16 min read
Save for later

Creating Joomla 1.5 Templates

Packt
27 Oct 2009
16 min read
Corporate Identity Corporate Identity (CI) refers to the self-image and the appearance of an enterprise. This appearance, the identity, either arises from the enterprise's tradition or it is completely invented in a newly created establishment. This identity is important to give the customer a feel for the enterprise and to enable recognition. Corporate Identity includes: Corporate Image (price, product, and advertising strategy) Corporate Design (visual appearance) Corporate Communication Corporate Behavior (behavior of employees towards each other and the outside world) All of the above areas have to be considered when developing a website. In this article, we will examine Corporate Design. At a minimum, it consists of a logo, a character font, and the house colors that the enterprise uses. The visitors to your website should recognize your enterprise on the first visit. HTML/XHTML, CSS, and XML The abbreviations HTML/XHTML, CSS, and XML stand for Internet technologies that Joomla! works with. The World Wide Web Consortium standardizes these technologies. HTML/XHTML The World Wide Web is based on HTML. HTML is not a programming language, but a text-description language. Each piece of text consists of structures like headings, lists, bold and italic areas, tables, and much more. HTML works with so-called tags. A tag has an opening portion and a closing portion. For example, a first-level heading looks as follows: <h1>This is a heading</h1> The tags are interpreted in the browser and the text is displayed according to their significance. HTML is very easy to learn and many online tutorials can be found. HTML is not being developed any further; the successor to HTML is XHTML version 1.0. CSS Cascading Style Sheets (CSS) are an extension to HTML. CSS is not a programming language either, but a vocabulary for defining the format properties of individual HTML elements. With the help of CSS commands, you can, for example, specify that the first-level headings should have a character size of 18 points in the character font Arial, are not bold, and have a spacing of 1.9 cm to the next paragraph. Such options are not possible with pure HTML and were not necessary when HTML was first developed. With the progressive commercialization of the Internet, additional formatting possibilities do, however, become more and more important. CSS data can be integrated into HTML in the following three ways: In the Central HTML File The CSS commands are defined in the head section of the HTML file like this: <head><title>title of the file</title><style type="text/css"><!--/* ... this is where the CSS commands are defined ... */--></style></head> In a Separate CSS File If the CSS commands apply to several HTML files, they can be stored in a separate file and the path to this file can be specified in the HTML header. This is the version that Joomla! uses. <head><title>title of the file</title><link rel="stylesheet" type="text/css" href="formats.css"></head> Within an HTML Tag CSS commands can also be inserted within an HTML tag: <body><h1 style="… CSS commands ...">...</h1></body> Combinations These three methods can be combined without any problem in an HTML file. It is, for instance, possible to subsequently overwrite CSS commands that were defined in a central file in the additional source code of an HTML page. This practice, however, quickly leads to confusing structures; it is better to customize the central file. XML The Extended Markup Language (XML) is a universe in itself. It represents a meta‑language and is derived from the much more complex SGML (Standardized Generalized Markup Language) that was developed in the sixties. XML is often used for configuration files and as an interchange format. For our purposes, you need XML as the description language for the metadata of the templates that you want to create. These metadata are primarily relevant for the Template Installer and the display in the Template Manager. In principle, these data also consist of opening and closing tags. For example: <name>Joomla! Book</name> The difference between HTML and XML is that no tags are predefined in XML. Because of that, you are completely unrestricted in the organization of the structures and the naming of the tags. Creating Your Own Templates Now we want to create our own template. There are several things to consider before we have a finished template packages. Let's take it one step at a time. Concept Before you start working, you have to create a concept. The work starts with a sketch or a diagram, especially when producing templates. It is up to you whether you want to create this sketch with an image editing program like Adobe Photoshop, Microsoft Paint that comes with Windows, the open-source program GIMP, or even with a piece of paper and crayons. Fixed Size or Variable (Fluid) or Both You can create two kinds of templates. Templates that adapt their structure to the size of the browser window and templates that have a fixed size. An example for the flexible layout: if you have 2048 pixels across your screen and the browser window is maximized, then your page is stretched accordingly. That can look strange if you use graphical, non-scalable elements like logos and signatures in your template. You have no control of what it is going to look like. Your other choice is to decide on a certain resolution and to position all the elements exactly on the pixels in the template. This has the advantage that your web page always looks the way you want. Unfortunately, you do not know the resolution of the monitor that is viewing your page. If that monitor has a resolution of 800 x 600 pixels, then your page fills the screen. On a large screen with a resolution of 1400 x 1050 pixels, it occupies about a quarter of the screen surface and looks a little lost. You will have to weigh the pros and cons and make a decision on one or the other, or you can consider barrier freedom and offer both versions. You must have seen websites where you can even change the font size. In addition to the font size buttons there is also often a button to select different layouts. If you prefer the fixed size, you should select a size that looks presentable on most screens, in other words 800 x 600 pixels. Since the browser has a scroll bar on the right side and the browser window is framed, the available width is even smaller, meaning that you have a maximum of 780 pixels to work with. Structure You are dealing with structured data and first have to determine a general allocation. Joomla! normally uses a structure as shown in the following figure: Section 1: Part 1: This is where your logo or a picture and the name of the website goes. Part 2: This is where the search field is. Part 3: This is where the linked navigation path goes (Breadcrumbs). Section 2: Part 4: The most important menus are shown in the left column. Part 5: The actual page content goes here. Part 6: The right column is a place for additional menus. Section 3: Part 7: The footer.     HTML Conversion Now you have to convert the concept into HTML and CSS. Depending on the graphics editing program that you have used to create it, there is a possibility that the picture can be automatically exported to HTML code. You can also do the conversion manually in a text editor, in an HTML editor like Dreamweaver (http://www.adobe.com/products/dreamweaver), or in one of the numerous free HTML editors (http://www.thefreecountry.com/webmaster/htmleditors.shtml). <table> or <div>? The <div> tag is a replacement and a supplement for the <table> tag in HTML. You can enclose several HTML elements, such as text and graphics in one common area with it. This general area does nothing for the time being but start in a new line of the continuing text. The <div> tag does not have any other properties. There are big benefits, however, in using a combination of <div> tags with CSS commands. <div> was specifically developed for the purpose of being formatted with CSS commands. Until 2004, it was common practice to define website structures with generous employment of HTML tables. With CSS and the <div> HTML element becoming more and more popular and with browsers being able to interpret these, more and more templates are being structured without HTML tables. However, rarely do we see websites that contain only semantically correct HTML and that have layouts that are built 100% without tables. The first step in structuring your website in that direction is the use of the <div> tags. Joomla! 1.5 is also gradually straying from the 'table path' and is starting to deliver semantically correct HTML. Nonetheless, it continues to be possible to structure your site layout with HTML tables. There are no table tags in the included rhuk_milkyway template; the entire table-like structure is created with <div> tags. Take a look at the original source code of this template to familiarize yourself with this technology. You can get more information about <div> tags at selfhtml (in German). Dreamweaver also supports this technology. The source code of the HTML conversion looks somewhat like the following listing. The code is kept simple on purpose and is not consistent with the XHTML standard in the header. The file name of this layout file has to be index.php since Joomla! searches it for embedded commands per PHP. HTML file /index.php: <html> <head> <link href="/joomla150/templates/joomla150buch/css/template.css" rel="stylesheet" type="text/css" /> </head> <body> <div id="part11">header / header<br /><br /> <div id="section1">section1</div> <div id="section2">section2</div> </div> <div id="part2">main display area / main<br /><br /> <div id="section3">breadcrumbs</div> <div id="section6">right side</div> <div id="section4">left side</div> <div id="section5">content</div> </div> <div id="part3">footer /footer<br /><br /> <div id="section7">section7</div> </div> </body></html> The subsequent CSS file from the individual template is integrated into the header area of the code. At the moment this CSS file contains only one command that defines the typeface. CSS file /css/template.css: body{font-size: 12px;font-family: Helvetica,Arial,sans-serif; }#Part1{ /*header*/float: left;border: 2px dotted green; }#Part2{ /*main*/float: left;border: 2px dotted yellow; }#Part3{ /*footer*/clear:all;border: 2px dotted red; }#Section1{ /*top right*/float: left; width: 18em;margin: 0 0 1.2em;border: 1px dashed silver; background-color: #eee; }#Section2{ /*top left*/float: right; width: 12em;margin: 0 0 1.1em;background-color: #eee; border: 1px dashed silver; }#Section3{ /*breadcrumbs*/border: 1px dashed silver;background-color: #eee; }#Section4{ /*left side*/float: left; width: 15em;margin: 0 0 1.2em;border: 1px dashed silver; }#Section5{ /*content*/margin: 0 12em 1em 16em;padding: 0 1em;border: 1px dashed silver; }#Section6{ /*right side*/float: right; width: 12em;margin: 0 0 1.1em;background-color: #eee; border: 1px dashed silver; }#Section7{ /*footer*/margin: 0 0 1.1em;background-color: #eee; border: 1px dashed silver; } You will create this first template manually in the Joomla! directory. When the template is ready, you can turn it into a compressed installation package that it can then be installed by a third party (or by you yourself) using the Joomla! installer. Save the HTML layout file by the name of index.php in the also newly created [PathtoJoomla]/templates/joomla150book/ directory. Save the template.css file in the [PathtoJoomla]/templates/joomla150book/css/ directory. The basic structure of your template is done. Now you have to define the template more exactly for Joomla! with the help of an XML file so that it will be displayed in the template administration section. Directory Structures of the Template Now it's time to take care of certain conventions. As previously discussed, the template has to be stored in a specific directory structure. [PathtoJoomla]/templates/[name of the template]/[PathtoJoomla]/templates/[name of the template]/CSS/[PathtoJoomla]/templates/[name of the template]/images/ The name of the template cannot contain blanks and other special characters. When this template is later installed as a package, the Template Installer has to create a directory from this name. Depending on the operating system, exotic combinations of characters can cause problems. In addition, the name should be meaningful. Here we will use joomla150book as the name of the template. Various files with predefined names have to be present in the template directories. Layout File: This is the HTML file that we created earlier:/templates/joomla150book/index.php. It should have the .php ending, since the dynamic Joomla! module elements that we will insert later have to be interpreted by PHP. Preview Picture: The /templates/joomla150book/template_thumbnail.png file contains a preview image of your template for preview selection in Joomla! administration in the Extensions | Template Manager menu. Preview pictures have a format of 200 by approximately 150 pixels. You can create this file later when you can see your template. Metadata of the Template: The /templates/joomla150_book/templateDetails.xml file represents the construction manual for the template installer and contains the installations for the template selection in the template manager. Here you specify the location where the files are to be copied, who the author is, and additional metadata about the template. During subsequent installation of this file by the Joomla! installer, PHP reads this file and copies the files to the place specified by the XML file. For the example template, you can use the file from the following listing (templateDetails.xml) and populate it with your own data. For every file that you use in the template, a respective XML container has to be populated with the file name and the correct path. <files> <filename> ... enter the filename of a file in the TemplateRoot directory ... </filename> <filename> ... for every file a filename-Container </filename></files> The other containers of the XML file are there for the description of the template. Here is the complete functional listing of the XML file: templateDetails.xml: <install version="1.5" type="template"><name>joomla150book</name><version>1.0</version><creationDate>11.11.2007</creationDate><author>Hagen Graf</author><copyright>GNU/GPL</copyright><authorEmail>hagen@cocoate.com</authorEmail><authorUrl>http://www.cocoate.com</authorUrl><version>0.1</version><description>... description</description><files><filename>index.php</filename><filename>templateDetails.xml</filename><filename>template_thumbnail.png</filename><filename>css/template.css</filename></files></install> Create the templateDetails.xml file in the [PathtoJoomla]/templates/joomla150book/ directory as well. CSS File: You can use several CSS files for your template. What name you give the CSS file and how you create it is up to you. There are, however, standard descriptions for various CSS elements. For your first attempt, you need a CSS file with the name /templates/joomla150_book/css/template.css. Graphics, Images: Here you can enter user-defined image files that you need in your template. The installer then copies the files into the images folder. The file name appears as /templates/joomla150_book/images/[user-defined image files]. First Trial Run Once you have reproduced all the structures in the [pathtoJoomla!]/templates/ subdirectory, you can already see your new template in the Extensions | Template Manager menu of your Joomla! administration and you can make it default: When you call up your website, you will see your new template. Unfortunately, there is no content shown yet. Since this content is produced dynamically, you have to integrate it piece by piece into your new template. Integration of the Joomla! Module The integration of the Joomla! module takes place by means of commands embedded into the HTML code. Joomla! uses the namespace jdoc to integrate various elements into the template. If you insert the following highlighted line into the header of the layout file: <head><jdoc:include type="head" /> </head> the title of the site and the news feed symbol are already correctly displayed. If you call up the source code of this site, you will notice that Joomla! has copied the entire metadata that you had entered in administration into the HTML code. In addition, the RSS feeds have been integrated by means of link tags and these will be displayed as feed symbols in browsers like Firefox that support this technology. Joomla! Metadata: <head> <base href="http://localhost/joomla150/" /> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="robots" content="index, follow" /> <meta name="keywords" content="joomla, joomla!, Joomla, Joomla!, J!" /> <meta name="description" content="Joomla! - dynamic portal-engine and Content-Management-System" /> <meta name="generator" content="Joomla! 1.5 - Open Source Content Management" /> <title>Welcome to the Frontpage</title> <link href="/joomla150/index.php?format=feed&amp;type=rss" rel="alternate" type="application/rss+xml" title="RSS 2.0" /> <link href="/joomla150/index.php?format=feed&amp;type=atom" rel="alternate" type="application/atom+xml" title="Atom 1.0" /> <script type="text/javascript" src="/joomla150/media/system/js/mootools.js"></script> <script type="text/javascript" src="/joomla150/media/system/js/caption.js"></script>...  
Read more
  • 0
  • 0
  • 2039

article-image-unity-game-development-welcome-3d-world
Packt
27 Oct 2009
21 min read
Save for later

Unity Game Development: Welcome to the 3D world

Packt
27 Oct 2009
21 min read
Getting to grips with 3D Let's take a look at the crucial elements of 3D worlds, and how Unity lets you develop games in the third dimension. Coordinates If you have worked with any 3D artworking application before, you'll likely be familiar with the concept of the Z-axis. The Z-axis, in addition to the existing X for horizontal and Y for vertical, represents depth. In 3D applications, you'll see information on objects laid out in X, Y, Z format—this is known as the Cartesian coordinate method. Dimensions, rotational values, and positions in the 3D world can all be described in this way. In any documentation of 3D, you'll see such information written with parenthesis, shown as follows: (10, 15, 10) This is mostly for neatness, and also due to the fact that in programming, these values must be written in this way. Regardless of their presentation, you can assume that any sets of three values separated by commas will be in X, Y, Z order. Local space versus World space A crucial concept to begin looking at is the difference between Local space and World space. In any 3D package, the world you will work in is technically infinite, and it can be difficult to keep track of the location of objects within it. In every 3D world, there is a point of origin, often referred to as zero, as it is represented by the position (0,0,0). All world positions of objects in 3D are relative to world zero. However, to make things simpler, we also use Local space (also known as Object space) to define object positions in relation to one another. Local space assumes that every object has its own zero point, which is the point from which its axis handles emerge. This is usually the center of the object, and by creating relationships between objects, we can compare their positions in relation to one another. Such relationships, known as parent-child relationships, mean that we can calculate distances from other objects using Local space, with the parent object's position becoming the new zero point for any of its child objects. Vectors You'll also see 3D vectors described in Cartesian coordinates. Like their 2D counterparts, 3D vectors are simply lines drawn in the 3D world that have a direction and a length. Vectors can be moved in world space, but remain unchanged themselves. Vectors are useful in a game engine context, as they allow us to calculate distances, relative angles between objects, and the direction of objects. Cameras Cameras are essential in the 3D world, as they act as the viewport for the screen. Having a pyramid-shaped field of vision, cameras can be placed at any point in the world, animated, or attached to characters or objects as part of a game scenario. With adjustable Field of Vision (FOV), 3D cameras are your viewport on the 3D world. In game engines, you'll notice that effects such as lighting, motion blurs, and other effects are applied to the camera to help with game simulation of a person's eye view of the world—you can even add a few cinematic effects that the human eye will never experience, such as lens flares when looking at the sun! Most modern 3D games utilize multiple cameras to show parts of the game world that the character camera is not currently looking at—like a 'cutaway' in cinematic terms. Unity does this with ease by allowing many cameras in a single scene, which can be scripted to act as the main camera at any point during runtime. Multiple cameras can also be used in a game to control the rendering of particular 2D and 3D elements separately as part of the optimization process. For example, objects may be grouped in layers, and cameras may be assigned to render objects in particular layers. This gives us more control over individual renders of certain elements in the game. Polygons, edges, vertices, and meshes In constructing 3D shapes, all objects are ultimately made up of interconnected 2D shapes known as polygons. On importing models from a modelling application, Unity converts all polygons to polygon triangles. Polygon triangles (also referred to as faces) are in turn made up of three connected edges. The locations at which these vertices meet are known as points or vertices. By knowing these locations, game engines are able to make calculations regarding the points of impact, known as collisions, when using complex collision detection with Mesh Colliders, such as in shooting games to detect the exact location at which a bullet has hit another object. By combining many linked polygons, 3D modelling applications allow us to build complex shapes, known as meshes. In addition to building 3D shapes, the data stored in meshes can have many other uses. For example, it can be used as surface navigational data by making objects in a game, by following the vertices. In game projects, it is crucial for the developer to understand the importance of polygon count. The polygon count is the total number of polygons, often in reference to a model, but also in reference to an entire game level. The higher the number of polygons, the more work your computer must do to render the objects onscreen. This is why, in the past decade or so, we've seen an increase in the level of detail from early 3D games to those of today—simply compare the visual detail in a game, such as Id's Quake (1996) with the details seen in a game, such as Epic's Gears Of War (2006). As a result of faster technology, game developers are now able to model 3D characters and worlds for games that contain a much higher polygon count and this trend will inevitably continue. Materials, textures, and shaders Materials are a common concept to all 3D applications, as they provide the means to set the visual appearance of a 3D model. From basic colors to reflective image-based surfaces, materials handle everything. Starting with a simple color and the option of using one or more images—known as textures—in a single material, the material works with the shader, which is a script in charge of the style of rendering. For example, in a reflective shader, the material will render reflections of surrounding objects, but maintain its color or the look of the image applied as its texture. In Unity, the use of materials is easy. Any materials created in your 3D modelling package will be imported and recreated automatically by the engine and created as assets to use later. You can also create your own materials from scratch, assigning images as texture files, and selecting a shader from a large library that comes built-in. You may also write your own shader scripts, or implement those written by members of the Unity community, giving you more freedom for expansion beyond the included set. Crucially, when creating textures for a game in a graphics package such as Photoshop, you must be aware of the resolution. Game textures are expected to be square, and sized to a power of 2. This means that numbers should run as follows: 128 x 128 256 x 256 512 x 512 1024 x 1024 Creating textures of these sizes will mean that they can be tiled successfully by the game engine. You should also be aware that the larger the texture file you use, the more processing power you'll be demanding from the player's computer. Therefore, always remember to try resizing your graphics to the smallest power of 2 dimensions possible, without sacrificing too much in the way of quality. Rigid Body physics For developers working with game engines, physics engines provide an accompanying way of simulating real-world responses for objects in games. In Unity, the game engine uses Nvidia's PhysX engine, a popular and highly accurate commercial physics engine. In game engines, there is no assumption that an object should be affected by physics—firstly because it requires a lot of processing power, and secondly because it simply doesn't make sense. For example, in a 3D driving game, it makes sense for the cars to be under the influence of the physics engine, but not the track or surrounding objects, such as trees, walls, and so on—they simply don't need to be. For this reason, when making games, a Rigid Body component is given to any object you want under the control of the physics engine. Physics engines for games use the Rigid Body dynamics system of creating realistic motion. This simply means that instead of objects being static in the 3D world, they can have the following properties: Mass Gravity Velocity Friction As the power of hardware and software increases, rigid body physics is becoming more widely applied in games, as it offers the potential for more varied and realistic simulation. Collision detection While more crucial in game engines than in 3D animation, collision detection is the way we analyze our 3D world for inter-object collisions. By giving an object a Collider component, we are effectively placing an invisible net around it. This net mimics its shape and is in charge of reporting any collisions with other colliders, making the game engine respond accordingly. For example, in a ten-pin bowling game, a simple spherical collider will surround the ball, while the pins themselves will have either a simple capsule collider, or for a more realistic collision, employ a Mesh collider. On impact, the colliders of any affected objects will report to the physics engine, which will dictate their reaction, based on the direction of impact, speed, and other factors. In this example, employing a mesh collider to fit exactly to the shape of the pin model would be more accurate but is more expensive in processing terms. This simply means that it demands more processing power from the computer, the cost of which is reflected in slower performance—hence the term expensive. Essential Unity concepts Unity makes the game production process simple by giving you a set of logical steps to build any conceivable game scenario. Renowned for being non-game-type specific, Unity offers you a blank canvas and a set of consistent procedures to let your imagination be the limit of your creativity. By establishing its use of the Game Object (GO) concept, you are able to break down parts of your game into easily manageable objects, which are made of many individual Component parts. By making individual objects within the game and introducing functionality to them with each component you add, you are able to infinitely expand your game in a logical progressive manner. Component parts in turn have variables—essentially settings to control them with. By adjusting these variables, you'll have complete control over the effect that Component has on your object. Let's take a look at a simple example. The Unity way If I wished to have a bouncing ball as part of a game, then I'd begin with a sphere. This can quickly be created from the Unity menus, and will give you a new Game Object with a sphere mesh (a net of a 3D shape), and a Renderer component to make it visible. Having created this, I can then add a Rigid body. A Rigidbody (Unity refers to most two-word phrases as a single word term) is a component which tells Unity to apply its physics engine to an object. With this comes mass, gravity, and the ability to apply forces to the object, either when the player commands it or simply when it collides with another object. Our sphere will now fall to the ground when the game runs, but how do we make it bounce? This is simple! The collider component has a variable called Physic Material—this is a setting for the Rigidbody, defining how it will react to other objects' surfaces. Here we can select Bouncy, an available preset, and voila! Our bouncing ball is complete, in only a few clicks. This streamlined approach for the most basic of tasks, such as the previous example, seems pedestrian at first. However, you'll soon find that by applying this approach to more complex tasks, they become very simple to achieve. Here is an overview of those key Unity concepts plus a few more. Assets These are the building blocks of all Unity projects. From graphics in the form of image files, through 3D models and sound files, Unity refers to the files you'll use to create your game as assets. This is why in any Unity project folder all files used are stored in a child folder named Assets. Scenes In Unity, you should think of scenes as individual levels, or areas of game content (such as menus). By constructing your game with many scenes, you'll be able to distribute loading times and test different parts of your game individually. Game Objects When an asset is used in a game scene, it becomes a new Game Object—referred to in Unity terms—especially in scripting—using the contracted term "GameObject". All GameObjects contain at least one component to begin with, that is, the Transform component. Transform simply tells the Unity engine the position, rotation, and scale of an object—all described in X, Y, Z coordinate (or in the case of scale, dimensional) order. In turn, the component can then be addressed in scripting in order to set an object's position, rotation, or scale. From this initial component, you will build upon game objects with further components adding required functionality to build every part of any game scenario you can imagine. Components Components come in various forms. They can be for creating behavior, defining appearance, and influencing other aspects of an object's function in the game. By 'attaching' components to an object, you can immediately apply new parts of the game engine to your object. Common components of game production come built-in with Unity, such as the Rigidbody component mentioned earlier, down to simpler elements such as lights, cameras, particle emitters, and more. To build further interactive elements of the game, you'll write scripts, which are treated as components in Unity. Scripts While being considered by Unity to be Components, scripts are an essential part of game production, and deserve a mention as a key concept. You can write scripts in JavaScript, but you should be aware that Unity offers you the opportunity to write in C# and Boo (a derivative of the Python language) also. I've chosen to demonstrate Unity with JavaScript, as it is a functional programming language, with a simple to follow syntax that some of you may already have encountered in other endeavors such as Adobe Flash development in ActionScript or in using JavaScript itself for web development. Unity does not require you to learn how the coding of its own engine works or how to modify it, but you will be utilizing scripting in almost every game scenario you develop. The beauty of using Unity scripting is that any script you write for your game will be straightforward enough after a few examples, as Unity has its own built-in Behavior class—a set of scripting instructions for you to call upon. For many new developers, getting to grips with scripting can be a daunting prospect, and one that threatens to put off new Unity users who are simply accustomed to design only. I will introduce scripting one step at a time, with a mind to showing you not only the importance, but also the power of effective scripting for your Unity games. To write scripts, you'll use Unity's standalone script editor. On Mac, this is an application called Unitron, and on PC, Uniscite. These separate applications can be found in the Unity application folder on your PC or Mac and will be launched any time you edit a new script or an existing one. Amending and saving scripts in the script editor will immediately update the script in Unity. You may also designate your own script editor in the Unity preferences if you wish. Prefabs Unity's development approach hinges around the GameObject concept, but it also has a clever way to store objects as assets to be reused in different parts of your game, and then 'spawned' or 'cloned' at any time. By creating complex objects with various components and settings, you'll be effectively building a template for something you may want to spawn multiple instances of, with each instance then being individually modifiable. Consider a crate as an example—you may have given the object in the game a mass, and written scripted behaviors for its destruction; chances are you'll want to use this object more than once in a game, and perhaps even in games other than the one it was designed for. Prefabs allow you to store the object, complete with components and current configuration. Comparable to the MovieClip concept in Adobe Flash, think of prefabs simply as empty containers that you can fill with objects to form a data template you'll likely recycle. The interface The Unity interface, like many other working environments, has a customizable layout. Consisting of several dockable spaces, you can pick which parts of the interface appear where. Let's take a look at a typical Unity layout: As the previous image demonstrates (PC version shown), there are five different elements you'll be dealing with: Scene [1]—where the game is constructed Hierarchy [2]—a list of GameObjects in the scene Inspector [3]—settings for currently selected asset/object Game [4]—the preview window, active only in play mode Project [5]—a list of your project's assets, acts as a library The Scene window and Hierarchy The Scene window is where you will build the entirety of your game project in Unity. This window offers a perspective (full 3D) view, which is switchable to orthographic (top down, side on, and front on) views. This acts as a fully rendered 'Editor' view of the game world you build. Dragging an asset to this window will make it an active game object. The Scene view is tied to the Hierarchy, which lists all active objects in the currently open scene in ascending alphabetical order. The Scene window is also accompanied by four useful control buttons, as shown in the previous image. Accessible from the keyboard using keys Q, W, E, and R, these keys perform the following operations: The Hand tool [Q]: This tools allows navigation of the Scene window. By itself, it allows you to drag around in the Scene window to pan your view. Holding down Alt with this tool selected will allow you to rotate your view, and holding the Command key (Apple) or Ctrl key (PC) will allow you to zoom. Holding the Shift key down also will speed up both of these functions. The Translate tool [W]: This is your active selection tool. As you can completely interact with the Scene window, selecting objects either in the Hierarchy or Scene means you'll be able to drag the object's axis handles in order to reposition them. The Rotate tool [E]: This works in the same way as Translate, using visual 'handles' to allow you to rotate your object around each axis. The Scale tool [R]: Again, this tool works as the Translate and Rotate tools do. It adjusts the size or scale of an object using visual handles. Having selected objects in either the Scene or Hierarchy, they immediately get selected in both. Selection of objects in this way will also show the properties of the object in the Inspector. Given that you may not be able to see an object you've selected in the Hierarchy in the Scene window, Unity also provides the use of the F key, to focus your Scene view on that object. Simply select an object from the Hierarchy, hover your mouse cursor over the Scene window, and press F. The Inspector Think of the Inspector as your personal toolkit to adjust every element of any game object or asset in your project. Much like the Property Inspector concept utilized by Adobe in Flash and Dreamweaver, this is a context-sensitive window. All this means is that whatever you select, the Inspector will change to show its relevant properties—it is sensitive to the context in which you are working. The Inspector will show every component part of anything you select, and allow you to adjust the variables of these components, using simple form elements such as text input boxes, slider scales, buttons, and drop-down menus. Many of these variables are tied into Unity's drag-and-drop system, which means that rather than selecting from a drop-down menu, if it is more convenient, you can drag-and-drop to choose settings. This window is not only for inspecting objects. It will also change to show the various options for your project when choosing them from the Edit menu, as it acts as an ideal space to show you preferences—changing back to showing component properties as soon as you reselect an object or asset. In this screenshot, the Inspector is showing properties for a target object in the game. The object itself features two components—Transform and Animation. The Inspector will allow you to make changes to settings in either of them. Also notice that to temporarily disable any component at any time—which will become very useful for testing and experimentation—you can simply deselect the box to the left of the component's name. Likewise, if you wish to switch off an entire object at a time, then you may deselect the box next to its name at the top of the Inspector window. The Project window The Project window is a direct view of the Assets folder of your project. Every Unity project is made up of a parent folder, containing three subfolders—Assets, Library, and while the Unity Editor is running, a Temp folder. Placing assets into the Assets folder means you'll immediately be able to see them in the Project window, and they'll also be automatically imported into your Unity project. Likewise, changing any asset located in the Assets folder, and resaving it from a third-party application, such as Photoshop, will cause Unity to reimport the asset, reflecting your changes immediately in your project and any active scenes that use that particular asset. It is important to remember that you should only alter asset locations and names using the Project window—using Finder (Mac) or Windows Explorer (PC) to do so may break connections in your Unity project. Therefore, to relocate or rename objects in your Assets folder, use Unity's Project window instead. The Project window is accompanied by a Create button. This allows the creation of any assets that can be made within Unity, for example, scripts, prefabs, and materials. The Game window The Game window is invoked by pressing the Play button and acts as a realistic test of your game. It also has settings for screen ratio, which will come in handy when testing how much of the player's view will be restricted in certain ratios, such as 4:3 (as opposed to wide) screen resolutions. Having pressed Play, it is crucial that you bear in mind the following advice: In play mode, the adjustments you make to any parts of your game scene are merely temporary—it is meant as a testing mode only, and when you press Play again to stop the game, all changes made during play mode will be undone. This can often trip up new users, so don't forget about it! The Game window can also be set to Maximize when you invoke play mode, giving you a better view of the game at nearly fullscreen—the window expands to fill the interface. It is worth noting that you can expand any part of the interface in this way, simply by hovering over the part you wish to expand and pressing the Space bar. Summary Here we have looked at the key concepts, you'll need to understand for developing games with Unity. Due to space constraints, I cannot cover everything in depth, as 3D development is a vast area of study. With this in mind, I strongly recommend you to continue to read more on the topics discussed in this article, in order to supplement your study of 3D development. Each individual piece of software you encounter will have its own dedicated tutorials and resources dedicated to learning it. If you wish to learn 3D artwork to complement your work in Unity, I recommend that you familiarize yourself with your chosen package, after researching the list of tools that work with the Unity pipeline and choosing which one suits you best.
Read more
  • 0
  • 0
  • 5294

article-image-understanding-model-development-process-ibm-cognos-8
Packt
27 Oct 2009
23 min read
Save for later

Understanding the Model Development Process in IBM Cognos 8

Packt
27 Oct 2009
23 min read
The process The Model Development Process is a proven step-by-step approach for designing and deploying planning models in an organization. This process enables us to chart various activities involved in identifying the organization's planning requirements in order to devise functional and efficient modeling solutions. The following diagram illustrates the Model Development Process and shows the typical stakeholders and IBM Cognos tools involved in the process: In the previous diagram, we saw four typical roles in organizations that are currently using the IBM Cognos Planning model and applications. They are described briefly as: Analyst Modeler: Responsible for gathering business requirements—designing, building, and testing Analyst models, and managing the data workflow within the model. System or Contributor Administrator: Responsible for creating, maintaining, and securing Contributor applications translated from Analyst models. Business Users: Responsible for entering, submitting, and reviewing planning data. Users will be referred to as the Business Users or Planners. Support Team: Responsible for maintaining models and applications, during or after the initial roll-out. Considerations for building an Analyst planning model When purchasing a vehicle, you may consider many attributes before finalizing your decision. For example, you may determine the type of vehicle to buy (sedan, minivan, and so on) or may evaluate the commuting needs. Likewise, before beginning to build the planning model, you must consider some key factors about our planning processes. To build and deploy the correct planning models in an organization, Project Managers, Business Users, Modelers, and other project stakeholders should consider the following factors at the initial stage of the planning project: Planning functional models Planning cycles and horizons Planning approaches Planning functional models Every business organization uses a variety of planning models to produce its business plans. A number of planning models are common in most of the organizations. For example, many business organizations have some form of revenue, cost of sales, payroll, capital, and operating expense models. On the other hand, some models are unique to a particular industry and trade. For example, a pharmaceutical company may have a Clinical trial or R&D model, or an international shipping company may need an aircraft fleet cost-control model. Other models may reflect an organization's business focus. The organization may develop a model to project and control a particular cost that is critical to its business strategy. For instance, a beverage company that places a heavy emphasis on brand recognition may have a separate marketing model, or a consulting company that routinely rotates its employees to offices around the world may have a separate travel model. Whatever purpose the models serve, it is important that you understand the rationale underlying the organization's use of them, so that you can build models that are more closely aligned to the organization's business needs. Planning cycles and horizons You also need to be aware of the organization's planning cycle and horizon. The planning cycle refers to the frequency by which an organization develops or updates its business plans. The planning horizon refers to how far into the future the organization plans. An organization may have multiple planning cycles, but may only plan for a single time horizon. The frequency with which an organization plans depends on many factors. For instance, organizations that operate in highly dynamic and competitive environments, such as technology companies, tend to have more frequent planning cycles. Companies in more stable environments, such as an alkaline batteries manufacturing company, tend to have less frequent cycles. Planning horizons may be driven by the organization's strategic focus or the nature of the business. For instance, the planning horizon of a pharmaceutical company's R&D plan may span up to 20 years, which is the amount of time that a clinical drug may take to get from inception to testing and eventually to marketability. A construction company may require multi-year plans to coincide with the time it takes to construct a building. More commonly, organizations develop a plan once a year in the form of an annual budget. The organization then revisits and calibrates the plan mid-year, after several months of actual data has been gathered. Actual data is used to measure year-to-date performance against the plan, so that the organization can forecast for the remainder of the year. The typical planning horizon is twelve months, usually the organization's fiscal year. If a long-range plan exists, the long-range plan is updated with changes to the annual plan or forecast. Planning cycle refers to the frequency at which an organization develops or updates its business plans. Planning horizon refers to how far into the future the organization plans. Knowing an organization's planning cycle and horizon is important when building a model. Many organizations use cycle-specific models because the business assumptions and calculations tend to differ between planning cycles. For instance, an organization can have a P&L model for the annual budget and another for the mid-year forecast because an annual budget and mid-year forecast usually require different data and calculation requirements. Knowing an organization's planning cycle can give you an insight into how you may want to build your models. The organization may start with detailed plans once or twice a year. If rolling forecasts are prepared, the forecasts may be done at a higher level, for instance, at an account or organizational summary level. This means that you may have to create a detailed model and a summary model. Knowing the planning horizon enables you to construct the appropriate timescale that can be used by other models. An organization that plans its revenue every quarter may also plan its expenses in the same way. An efficient planning model is built on standard data structures, such as timescale. Thus, timescales are an important consideration because they can be shared across several of the organization's planning models. Planning approaches Business organizations can use different approaches to plan their budgets and forecasts. You need to consider these approaches when building the model, as these approaches dictate how the model will be designed and deployed. Examples of common approaches are as follows: Zero-based budgeting: Each planner prepares estimates of their proposed revenue or expenses for a specific period of time as if they were planning for the first time. By starting from scratch at each budget cycle, for example, managers are required to take a closer look at all of their revenues and expenses. Driver based: Driver based planning models typically calculate plan numbers by adding, subtracting, or multiplying various drivers or metrics. Examples of drivers: number of units sold, price of a product, and so on. Top-down: Top Upper-level management sets the targets and pushes them to lower management who then pushes them further down the organization. Then the plans for achieving the targets are submitted up the chain of command for review and approval. Bottom-up: Lower-level management prepares the plans and then submits them up the chain of command for review and approval. The approval and rejection process follows until the plan and finalized. Designing the model template in Analyst A planning model is a set of Analyst objects whose purpose is to generate specific plans using a variety of data inputs, assumptions, and calculations.  In practice, a model is named after the output it produces. An output can be a specific budget for product lines or it can be a category of expenses consisting of several general ledger accounts, such as payroll. Once you have identified the model output, break it down into its inputs, assumptions, and calculations. For example, a salary plan may be the outcome of the inputs of employees and positions, their current salary, earned merit increases, and bonuses. The salary for newly-hired staff may be assumed based on their position. To produce the salary plan, the model would calculate the merit increases and bonuses for each employee by multiplying the salary by the merit and bonus percentages and then by adding the results to the salary. Then it would pull the appropriate salary for each new hire depending on position. Finally, the model would aggregate all of the employees' and new hires' salaries to come up with the salary plan. In this simplified example, four model functions are apparent: inputs, assumptions, calculations, and outputs. In fact, you can say that a model is a collection of these four functions. The IBM Cognos Planning Analyst tool allows you to build objects that collect inputs from users, designate assumed values, and perform calculations on them in order to produce the expected output. Flowcharting the model structure Before building an effective planning model, it is important to develop a detailed flowchart that logically illustrates all of the model's structural components. Just as an architect develops a building's blueprints before even breaking the ground, you must begin with the model's blueprints. Often, many modelers skip this important step and begin constructing the objects, without a clear path to the final outcome. Unfortunately, such haste results in a disorganized and inefficient model. A poorly-designed model can adversely impact an application's performance and cause a downstream effect on user productivity. The consequence can be severe. When the model is deployed to hundreds or thousands of users, a single instance of inefficiency will multiply at an equivalent scale. Flowcharting helps you to avoid these problems. It gives you a glimpse of the final product and forces you to think through the various factors and issues that must be addressed before starting to build the model. A disciplined and methodical approach can steer you away from many of model building's hidden pitfalls. Indeed, a well thought out flowchart can cut the build time significantly by minimizing rework and trial and error. Flowcharting can lead you to uncovering the important design elements, such as the dimensions, datastore, and data flow. A good flowchart should show the sources of data inputs, and whether they are entered by the planner or originate from other data sources such as an ERP system or a general ledger system. The flowchart should also illustrate the way that data will be stored and used, how it enters the model, and how it flows from source to target. Finally, the flowchart should describe the different ways in which data can be viewed so that you can gather the various dimensions that need to be included in the model. For instance, data can be viewed by cost center, departments, or profit centers. Alternatively, it can be viewed across time (days, weeks, months, years) or by versions (this year, last year, plan, scenarios). Some developers may refer to model flowcharts as model schematics or Data Flow Diagrams (DFD). You, the Modeler, typically initiate this design step in the model development process after learning and understanding the key business planning requirements. You then 'white-board' the design of the model template, and then document the design specification in a document called a Detailed Design Specification (DDS). Finally, you take the design specification and implement it in IBM Cognos Planning Analyst using the Analyst's features and functionality. The concept of multi dimensionality IBM Cognos Planning is based on a multi-dimensional data structure in which data is organized around specific attributes, or dimensions. In the following table, data is organized around Account, Year, Version, Cost Center, and Month. Each record in the table contains data by account, year, version, cost center, and month. One of the most common ways of presenting multi-dimensional data is in the form of a cube. In a multi-dimensional cube, data is displayed as one slice at a time along two or more dimensions. Each slice represents a subset of the population. Those familiar with Excel pivot tables should have little problem grasping this concept. However, those who are only familiar with spreadsheets can still find some similarities. In a spreadsheet, the rows and columns are actually two separate dimensions. A third dimension, the worksheet, gives you a three-dimensional view of data. If you enter data into the first cell in a spreadsheet, you are actually entering the data along three dimensions—Sheet 1, Column A, and Row 1. Hence, when you reference that cell, Excel denotes it as      Sheet1!A1. A multi-dimensional cube lets you view data the same way. But a cube can have several dimensions. Each dimension contains a list of related data such as accounts, version, cost center, or time period. When two or more dimensions intersect, the intersection represents a record or view of the data. For instance, a cost center dimension may list all the cost centers in the organization. A second dimension lists a group of expense accounts, a third lists 12 months, and a fourth lists the version (Plan or Actual). The intersection of these dimensions gives you data by cost center, by account, by month, and by version. The following Excel pivot table is an example of a multi-dimensional cube. Here you see a slice of the cube with the following dimensions: Account, Cost Center, Month, and Version. In a multi-dimensional cube, you can arrange data in a variety of ways by swapping rows, columns, and pages. This is a powerful feature that facilitates in-depth data analysis. Those who have worked with multi-dimensional cubes understand their benefits. Multi-dimensional cubes can help you sift through masses of data to find valuable information. IBM Cognos Planning takes multi dimensionality a step further by leveraging its features to enforce rules and standards in order to make model maintenance easier. Analyst is the tool that lets you create the planning template that the users will use to enter their plans, while Contributor is the tool that lets you replicate the templates and deploy them to a number of users based on a defined hierarchy. The plans are stored in a central database, and users connect to it through the Web. In a spreadsheet environment, similarities exist. You have a master template that you can use to build the worksheets. The worksheets are stored in a central folder, within sub-folders that are organized according to a hierarchy. Users connect to the shared folder to access their worksheets.     Understanding dimensions, datastore, and data flow Analyst objects are the building blocks of the planning model. These objects enable you to define the data structure, store and calculate the data, and move data from source to targets. There are a host of objects in Analyst, each offering useful capabilities. However, the key objects are the D-List, D-Cube, and D-Link. These objects are indispensable to a model and thus deserve special attention. Determining dimensions: D-List The D-List is the basic building block of the model. In Analyst, dimensions are referred to as D-Lists. Each item in a D-List represents an attribute of the data. In a D-List, we decide what data to include in the model and how the data will behave. The data could be something that will be entered by the planner; it could be pre-populated, or it could be calculated. For example, to build a model of your personal expenses, you may have a list of expense categories (travel, food, and entertainment), you may want to track your spending over time (month, quarter, and year), and you may want to compare different versions of spending (actual and planned). Each of these lists of items could be a D-List. In the Spending Category D-List, you might include a Total that sums up Travel, Food, and Entertainment (see the following screenshot). In the Versions D-List, you may want a "Variance" between actual and planned values. There is virtually no restriction to the type of data that you can include. However there are certain principles to adhere to when creating D-Lists. The first step in constructing a model is to identify the dimensions that will be used. There are many sources of information that will give you an idea of the dimensions that you need. Data entry templates from the organization's existing planning systems or Excel spreadsheets can suggest many ways in which data is gathered. The spreadsheet can also reveal the calculations used. Performance reports can be used to determine what the model outputs will be. Often, simply inquiring about the business can be a good start. Consider that you're working on a project that requires you to design and build a revenue forecasting model for a Fortune 100 global consumer electronics retailer. One approach to determining the dimensions of this forecasting model is to ask the following questions: What does the company sell? The dimensions could contain a list of consumer electronics products, such as MP3 players and laptops, product categories such as audio and computers, or even brands. Who is the company selling to? The retailer's customer list could be a dimension. Where does the company operate? Dimensions may contain a list stores, states, cities, countries, global regions, or market segments. What is the forecasting timeline? The timeline dimensions may be weeks, months, quarters, or years. The words "D-List" and "dimension" are often used interchangeably. When used in the context of a cube, "dimension" is often more appropriate. Building the datastore: D-Cubes Whereas the D-List is where the data is defined, the D-Cube is where the data is stored. After you have decided what data will be included in the model, you determine how the data will be stored. The D-Cube is formed by two or more D-Lists. A typical planning model consists of several cubes. The cubes store a particular set of data and perform a specific function. For example, an Employee cube may store data about employees. A P&L cube may contain revenue and expense data. D-Cubes can be functionally classified as either an input cube that allows data entry, a calculation cube that processes data, or an output cube that displays the result. The Employee cube can be broken into an Employee Input cube (see the following example), Employee Calculation cube, and Employee Summary cube. The words "D-Cube" and "cube" are often used interchangeably. Except for the terminology, there is no distinction between the two. "D-Cubes" are usually used in an Analyst setting, but "cubes" can work as well. The key to building D-Cubes is to understand their primary function. Is the cube a place where planners will enter data? Will it be used simply to stage data? Will it be used to calculate inputs and feed the result somewhere else? Will it be used to present data in a report format for reviewers? These important questions must be answered before building the cube. Another factor to think about is data. Data is stored in a cube. Consequently, the cube structure needs to follow the format of the data source that will be feeding it. As a modeler, you need to understand what type of data will be going into the model. For instance, planners need data to compare and analyze planning and actual information. They would like to see actual year-to-date sales compared to next-year projections. During the initial design process, you may decide to work with the data provider to review the source data and develop a process to extract, load, and validate data in planning models. Perhaps the most important consideration is size. In a multi-dimensional data structure, size is always a constraint. Size has a direct impact on performance; the greater the size, the more time it will take to process data and transmit it over the web. In fact, performance can be such a tremendous constraint that it affects the way the model is designed. Controlling data flow: D-Links In a model that shares data among several cubes, data must flow from one cube to another. The D-link is an object that moves data. Similar to a data transformation or ETL tool, the D-Link maps dimension items in the source to dimension items in the target, enabling you to control the flow of data within the model. For multi-dimensional cubes where data sparseness can be a problem, the D-Link has a practical purpose. The D-Link allows you to break a large cube into smaller, specialized cubes while still making the same data available. Most models use function-specific cubes, where outputs from one cube are inputs to another. The D-Link connects input, calculation, and output cubes, bringing them together to allow the seamless movement of data. Any cube that requires data in order to perform its function can retrieve data without going outside of the model. Because data can be reused, it only needs to enter the model once, thereby simplifying the data import process. The D-Link's ability to transport data is not limited to cubes. D-links can import data from a database, an ASCII file, an Excel spreadsheet, or a Contributor application. The words "D-Link" and "link" are often used interchangeably. Except for the terminology, there is no distinction between the two. "D-Link" is usually used in the context of Analyst. What makes an optimal model? The saying goes: "There is more than one way to skin a cat." The same can be said about model building. There are myriad ways to create the same output by using a combination of inputs, assumptions, and calculations. IBM Cognos Planning allows you to create highly complex models using its advanced forecasting algorithms and scenario planning facilities. With this capability at your disposal, you may be tempted to build a model that "does everything at the push of the button". While such automation can appear impressive, it is often accompanied with many problems. Complex models make ownership and maintenance difficult. A highly-customized model can become so inflexible that when it's time to enhance it, starting from scratch is an easier option, rather than building on its current form. Support and maintenance can also become a nightmare when you need to go through a laundry list of tasks to prepare for the next cycle. The tendency towards over-automation and over-customization, must be tempered with caution. More often than not, the model that "does everything" also requires everything to support and maintain it. So what is an optimal model? The answer is one that delivers planning information in a timely manner at the lowest possible cost. Although delivering better information has always been at the forefront of every planning project, the cost of delivering it tends to be elusive. To be sure, the financial cost of the system is closely monitored, but there are costs hidden within the system's inner workings that cannot be quantified and are often left to persist. The cost can take many forms: What is the cost of a poorly designed model? What is the cost of a Contributor application taking twice as long to process? What is the cost of thousands of users waiting an extra 10 seconds each time they can download a planning model? These costs must be taken into consideration when building the model. You, as a Modeler, must not only build a model that does its job, you must do so without placing an undue burden on these cost factors. Principles of model building If you ask ten people what makes an optimal model, you are likely to get ten different answers. This is not surprising. The quest for the one-size-fits-all formula has been a long one, owing mostly to the differences in the ways that organizations plan, but also to the openness of the tool and the absence of a shared body of knowledge. Although there are no hard and fast rules, there are three guiding principles that can help lead you down the correct path. Efficiency Performance Maintenance Efficiency An optimal model must be built with an eye towards efficiency. An efficient model is one that takes the shortest path to performing its task. Usually this means fewer objects in the model. But it could mean other things: Data flows in one direction, D-Cubes perform clear and specific functions, calculations are more intuitive and easy to understand, D-Lists contain as few dimension items as possible, redundancies are non-existent, and data is organized in a logical fashion. Efficiency and simplicity go hand-in-hand. Simplicity eliminates clutter. It begs the question: Is this absolutely necessary? To a savvy Modeler, the concept of simplicity may be counter-intuitive and run contrary to his nature. Yet the ability to take complex processes and re-engineer them down to a few moving parts is indispensable to model building. Indeed, it is a higher skill, one that compels you to abandon conventional wisdom, think out of the box, and explore unfamiliar territories. Performance An optimal model is one that performs its task faster using the same resources. Performance combines effectiveness with timeliness. This means delivering the right information at the right time. The model must be able to process data and respond to user requests within reasonable time and without unnecessary delays. Although not everyone will agree on what "reasonable time" means, everyone can agree on what constitutes "unnecessary delays". It is the difference between how the model performs and how it should perform. A model that is built on a weak foundation almost always bears extra processing overhead that takes additional time. There are essentially three areas where performance is most visible: Application processing Web client access Web client processing Application processing refers to the server batch process that implements changes to the model, or loads data. Web client access is the point where users connect to the database to retrieve or save their plans. Web client processing is where users actually work with their planning templates, entering data and switching from cube to cube. All of these areas have a direct impact on user productivity, so that any lag in performance creates cost in some form. Maintenance An optimal model is one that requires the least amount of effort to set up and maintain. In a constantly-changing business landscape, organizations must be able to adapt to new environments quickly. Competitive pressures may push organizations to shorten their planning cycles or drive them to a new strategic direction. Planning models must reflect new realities in order to accurately project the future. They must therefore be flexible and easy to maintain. An optimal model is built on the premise that change is constant. The model must allow for its assumptions and calculations to change without a complete overhaul. It must use standards and share objects so that changes can cascade rapidly throughout its various parts. The model should enable a non-developer to easily take ownership of it without the need for advanced training. These principles can be self-reinforcing. For instance, an efficient model usually performs faster and is easier to maintain. However, they are not exclusive and trade-offs can occur. When two good approaches contradict, you must weigh the benefit of one over the other and accept the trade-off. In a way, modeling is an art. No strict rules govern how a model should be built, lending the entire exercise to one's own creativity. As a modeler, you should look to these principles for guidance, while keeping a close watch on other factors. In the final analysis, the planning system, like any other system, must be viewed in the light of its benefits, as well as its cost.  
Read more
  • 0
  • 0
  • 5771

article-image-how-create-tax-rule-magento
Packt
27 Oct 2009
11 min read
Save for later

How to create a Tax Rule in Magento

Packt
27 Oct 2009
11 min read
In this article by William Rice, we will see how to create Tax Rules in Magento. In the real world, the tax rate that you pay is based on three things: location, product type, and purchaser type. In Magento, we can create Tax Rules that determine the amount of tax that a customer pays, based upon the shipping address, product class, and customer class. When you buy a product, you sometimes pay sales tax on that product. The sales tax that you pay is based on: Where you purchased the product from. Tax rules vary in different cities, states, and countries. The type of product that you purchased. For example, many places don't tax clothing purchases. And, some places tax only some kinds of clothing. This means that you must be able to apply different tax rates to different kinds of products. The type of purchaser you are. For example, if you buy a laser printer for your home, it is likely that you will pay sales tax. This is because you are a retail customer. If you buy the same printer for your business, in most places you will not pay sales tax. This is because you are a business customer. The amount of the purchase. For example, some places tax clothing purchases only above a specific amount. Anatomy of a Tax Rule A Tax Rule is a combination of the tax rate, shipping address, product class, customer class, and amount of purchase. A Tax Rule states that you pay this amount of tax if you are this class of purchaser, and you bought this class of product for this amount, and are shipping it to this place. The components of a Tax Rule are shown in the following screenshot. This screen is found under Sales | Tax | Manage Tax Rules | Add New Tax Rule. You will see the Name of the Tax Rule while working in the backend. Customer Tax Class Customer Tax Class is a type of customer that is making a purchase. Before creating a Tax Rule, you will need to have at least one Customer Tax Class. Magento provides you with a Tax Rule called Retail Customer. If you serve different types of customers—retail, business, and nonprofit—you will need to create different Customer Tax Classes. Product Tax Class Product Tax Class is a type of Product that is being purchased. When you create a Product, you will assign a Product Tax Class to that Product. Magento comes with two Product Tax Classes:Taxable Goods and Shipping. The class Shipping is applied to shipping charges because some places charge sales tax on shipping. If your customer's sales tax is different for different types of Products, then you will need to create a Product Tax Class for each type of Product. Tax Rate Tax Rate is a combination of place, or tax zone, and percentage. A zone can be a country, state, or zip code. Each zone that you specify can have up to five sales tax percentages. For example, in the default installation of Magento, there is one tax rate for the zone New York. This is 8.3750 percent, and applies to retail customers. The following window can be found at Sales | Tax | Manage Tax Zones & Rates and then clicking on US-NY-*-Rate 1: So in the screenshot of our Tax Rule, the Tax Rate US-NY-*-Rate 1 doesn't mean "a sales tax of 1 percent." It means "Tax rate number 1 for New York, which is 8.3750 percent." In this scenario, New York charges 8.3750 percent sales tax on retail sales. If New York does not charge sales tax for wholesale customers, and you sell to wholesale customers, then you will need to create another Tax Rate for New York: Whenever a zone has different sales taxes for different types of products or customers, you will need to create different Tax Rates for that zone. Priority If several Tax Rules try to apply several Tax Rates at the same time, how should Magento handle them? Should it add them all together? Or, should it apply one rate, calculate the total, and then apply the second rate to that total? That is, should Magento add them or compound them? For example, suppose you sell a product in Philadelphia, Pennsylvania. Further suppose that according to the Tax Rule for Pennsylvania, the sales tax for that item is 6 percent, and that the Tax Rule for Philadelphia adds another 1 percent. In this case, you want Magento to add the two sales taxes. So, you would give the two Tax Rates the same Priority. By contrast, Tax Rates that belong to Tax Rules with different Priorities are compounded. The Tax Rate with the higher Priority (the lower number) is applied, and the next higher Priority is applied to that total, and so on. Sort Order Sort Order determines the Tax Rules' position in the list of Tax Rules. Why create Tax Rules now? Why create a Tax Rule now, before adding our first Product? When you add a Product to your store, you put that Product into a Category, assign an Attribute Set, and select a Tax Class for that Product. By default, Magento comes with two Product Tax Classes and one Tax Rule already created. The Product Tax Classes are Taxable Goods and Shipping. The Tax Rule is Retail Customer-Taxable Goods-Rate 1. If you sell anything other than taxable goods, or sell to anyone other than retail customers, you will need to create a new Tax Rule to cover that situation. Creating a Tax Rule The process for creating a Tax Rule is: Create the Customer Tax Classes that you need, or confirm that you have them. Create the Product Tax Classes that you need, or confirm that you have them. Create the Tax Rates that you need, or confirm that you have them and that they apply to the zones that you need. Create and name the Tax Rule: Assign Customer Tax Class, Product Tax Class, and Tax Rates to the Rule. Use the Priority to determine whether the Rule is added, or compounded, with other Rules. Determine the Sort Order of the Rule and save it. Each of these steps is covered in the subsections that follow. Time for action: Creating a Customer Tax Class From the Admin Panel, select Sales | Tax | Customer Tax Classes. The Customer Tax Classes page is displayed. If this is a new installation, only one Class is listed, Retail Customer as shown in the following screenshot: Click on Add New. A Customer Tax Class Information page is displayed. Enter a name for the Customer Tax Class. In our demo store, we are going to create Customer Tax Classes for Business and Nonprofit customers. Click on Save Class. Repeat these steps until all of the Customer Tax Classes that you need have been created. What just happened? A Tax Rule is composed of a Customer Class, Product Class, Tax Rate, and the location of the purchaser. You have just created the first part of that formula: the Customer Class. Time for action: Creating a Product Tax Class From the Admin Panel, select Sales | Tax | Product Tax Classes. The Product Tax Classes page is displayed. If this is a new installation, only two Classes are listed: Shipping and Taxable Goods. Click on Add New. The Product Tax Class Information page gets displayed: Enter a name for the Product Tax Class. In our demo store, we are going to create Product Tax Classes for Food and Nonfood products. We will apply the Food class to the coffee that we sell. We will apply the Nonfood class to the mugs, coffee presses, and other coffee accessories that we sell. Click on Save Class. Repeat these steps until all of the Product Tax Classes that you need have been created. What just happened? A Tax Rule is composed of a Customer Class, Product Class, Tax Rate, and the location of the purchaser. You have just created the second part of that formula: the Product Class. Creating Tax Rates In Magento, you can create Tax Rates one at a time. You can also import Tax Rates in bulk. Each method is covered in the next section. Time for action: Creating a Tax Rate in Magento From the Admin Panel, select Sales | Tax | Manage Tax Zones & Rates. The Manage Tax Rates page is displayed. If this is a new installation, only two Tax Rates are listed: US-CA-*-Rate 1 and US-NY-*-Rate 1. Click on Add New Tax Rate. The Add New Tax Rate page gets displayed: Tax Identifier is the name that you give this Tax Rate. You will see this name when you select this Tax Rate. The example that we saw is named US-CA-*-Rate 1. Notice how this name tells you the Country, State, and Zip/Post code for the Tax Rate. (The asterisk indicates that it applies to all zip codes in California.) It also tells which rate applies. Notice that the name doesn't give the actual percentage, which is 8.25%. Instead, it says Rate 1. This is because the percentage can change when California changes its tax rate. If you include the actual rate in the name, you would need to rename this Tax Rate when California changes the rate. Another way this rate could have been named is US-CA-All- Retail. Before creating new Tax Rates, you should develop a naming scheme that works for you and your business. Country, State, and Zip/Post Code determine the zone to which this Tax Rate applies. Magento calculates sales tax based upon the billing address, and not the shipping address. Country and State are drop-down lists. You must select from the options given to you. Zip/Post Code accepts both numbers and letters. You can enter an asterisk in this field and it will be a wild card. That is, the rate will apply to all zip/post codes in the selected country and state. You can enter a zip/post code without entering a country or state. If you do this, you should be sure that zip/post code is unique in the entire world. Suppose you have one tax rate for all zip codes in a country/state, such as 6% for United States/Pennsylvania. Also, suppose that you want to have a different tax rate for a few zip codes in that state. In this case, you would create separate tax rates for those few zip codes. The rates for the specific zip codes would override the rates for the wild card. So in a Tax Rate, a wild card means, "All zones unless this is overridden by a specific zone." In our demo store, we are going to create a Tax Rate for retail customers who live in the state of Pennsylvania, but not in the city of Philadelphia as shown: Click on Save Rate. You are taken back to the Manage Tax Rates page. The Tax Rate that you just added should be listed on the page. This procedure is useful for adding Tax Rates one at a time. However, if you need to add many Tax Rates at once, you will probably want to use the Import Tax Rates feature. This enables you to import a .csv, or a text-only file. You usually create the file in a spreadsheet such as OpenOffice Calc or Excel. The next section covers importing Tax Rates. What just happened? A Tax Rule is composed of a Customer Class, Product Class, Tax Rate, and the location of the purchaser. You have just created the third part of that formula: the Tax Rate. The Tax Rate included the location and the percentage of tax. You created the Tax Rate by manually entering the information into the system, which is suitable if you don't have too many Tax Rates to type. Time for action: Exporting and importing Tax Rates In my demo store, I have created a Tax Rate for the state of Pennsylvania. The Tax Rate for the city of Philadelphia is different. However, Magento doesn't enable me to choose a separate Tax Rate based on the city. So I must create a Tax Rate for each zip code in the city of Philadelphia. At this time there are 84 zip codes, and are shown here:     19019 19092 19093 19099 19101 19102 19103 19104 19105 19106 19107 19108 19109 19110 19111 19112 19113 19114 19115 19116 19118 19119 19120 19121 19122 19123 19124 19125 19126 19127 19128 19129 19130 19131 19132 19133 19134 19135 19136 19137 19138 19139 19140 19141 19142 19143 19144 19145 19146 19147 19148 19149 19150 19151 19152 19153 19154 19155 19160 19161 19162 19170 19171 19172 19173 19175 19177 19178 19179 19181 19182 19183 19184 19185 19187 19188 19191 19192 19193 19194 19196 19197 19244 19255        
Read more
  • 0
  • 0
  • 12449

article-image-podcasting-and-images-drupal
Packt
27 Oct 2009
8 min read
Save for later

Podcasting and Images in Drupal

Packt
27 Oct 2009
8 min read
Getting Started with Podcasts To create a podcast, you will need: A mp3 file A place to store the mp3 file At the risk of stating the obvious, a good podcast requires thought and planning before you make the actual recording. Later in the article, we will discuss some of these general mechanics. But, from a technical perspective, once you have your audio file, you can upload it to your Drupal site, and you will have published a podcast. Audio Module The Audio module supports the playback of audio files that have been uploaded to your site. To install this module, we will also need to install two helper modules required by the Audio module: the getID3() and Token modules. In this section, we will cover installing the Audio module, as well as the getID3() and Token modules. Install the getID3() Module Download the getID3() module from http://drupal.org/project/getid3, and upload it to your sites/all/modules directory . Do not, however, enable the module, as we need to install an additional piece of code described as follows. Install the getID3() Libraries The getID3() libraries are a tool that automatically extract information about audio files. These libraries don't require you to do any additional work; rather, they detect information that can be used by the Audio module. Download the getID3() libraries from http://getid3.sourceforge.net/. Unzip these libraries onto your hard drive. As shown in the preceding screenshot, the libraries include some demo and helper files, in addition to the readme and license files. The only files we need are contained in the getid3 directory. The getid3 directory is the only directory that you need to upload to your website. Then, use your FTP client to connect to your web server, and navigate to sites/all/modules/getid3. Upload the getid3 directory into sites/all/modules/getid3 as shown in the following screenshot: Once the module and the libraries have been uploaded to your site, enable the getID3() module by clicking the Administration | Site building | Modules link, or by navigating to admin/build/modules. Following these instructions the path to your getID3() library is sites/all/modules/getid3/getid3. If needed, this path can be adjusted at Administer | Site configuration | getID3(), or admin/settings/getid3. Install the Token Module Download the Token module from http://drupal.org/project/token, and install it. Once this module has been uploaded to your site, enable it by clicking the Administration | Site building | Modules link, or by navigating to admin/build/modules. The Token module is a helper module, and its functionality will be largely invisible to the end user. The Token module supplies pieces of text, or tokens, which can be used by other modules. The Audio module relies on the Token module and the getID3() module to help automatically generate titles and other information for audio files. Install and Enable the Audio Module Download the audio module from http://drupal.org/project/audio. Upload the module to your sites/all/modules directory, and enable it by clicking the Administer | Site building | Modules link or by navigating to admin/build/modules. Select the Audio and the Audio getID3 modules. Click the Save configuration button to submit the form and enable the modules. Configure the Audio Module Now that we have installed the Audio module and its helper modules, we need to configure the audio module to support our needs. Click the Administer | Site Configuration | Audio link, or navigate to admin/settings/audio. As pictured in the following screenshot, you will see three tabs across the top of the page: Audio, Metadata tags, and Players. The Audio Tab The options on the Audio tab, pictured in the preceding screenshot, allow you to set some default values that are used when audio posts are uploaded. The values here can be created automatically, which can be useful if you are working with songs. For most cases, however, you will want to delete the option for the Default node title format, and leave the other default values intact. When you have adjusted the settings, click the Save configuration button at the bottom of the page. To save your settings, you must click the Save configuration button before moving on to the next tab. A Brief Explanation of Tokens In the preceding screenshot, there is a collapsible fieldset titled List of available tokens. Click on the link to expand the fieldset. A portion of the tokens available are shown in the following screenshot: As suggested by the preceding screenshot, tokens expose pieces of information about content created within a site. Tokens can only be used when a module has been written to work with the tokens. Because the Audio module has been written to depend on the Token module, we have the option of using tokens if we wish. For example, we could set the title of audio nodes to automatically incorporate the username and the creation date. To make this work, we would set the Default node title format (as shown in the Audio settings screenshot) to Created by [author-name] on [yyyy]-[mon]-[date]. In most cases tokens run invisibly in the background without requiring any adjustments by the end user. The Metadata Tags Tab The options in this section will be useful if you are setting up podcasts as part of a music or radio station, but will be less useful in other environments. By reducing the number of required options, you can simplify the form for uploading podcasts. The settings pictured in the following screenshot are all you need to get started publishing audio on the web. The Players Tab The Audio module comes with several different players that can be used to play your audio files. You can use the settings on this page to choose your preferred player. As you can see in the following figure, you can specify a different player for each type of audio file. The "best" player will largely be determined by your aesthetic preference; all of the players do a great job playing audio stored on your site. After you have chosen a player, click the Save configuration button to save your preference. Assign Rights to the Audio Module Now that we have installed, enabled, and configured the audio module, we need to assign rights to it. Click the Administer | User management | Roles link, or navigate to admin/user/roles. The possible rights that can be assigned are shown in the following figure: We will need to assign rights for the teacher role, the student role, the authenticated user role, and possibly the anonymous user role. For the authenticated user role, assign rights to download audio and play audio. For the student role, assign rights to create audio and edit own audio. For the teacher role, assign rights to create audio, edit own audio, and view download stats. For the anonymous user role, assign the rights you think are appropriate. In most cases, if you are allowing anonymous users to see content, allowing them the rights to download audio and play audio is appropriate. Each time you assign rights to an individual roles, click the Save permissions button to save the rights for the role. Adjust Existing Views Currently, three views are being used to display student and teacher-created content. We will need to edit these views so that they return any audio nodes created within the site. To edit these views, click the Administer | Site building | Views link, or navigate to admin/build/views. We need to edit three views: the teacher_blog view, the student_blog and conversation views. As shown in the following screenshot, these views can be edited by using the Edit link on the main Views administration page. Editing the student_blog View Click the Edit link as shown in the preceding screenshot. Then, in the Defaults display, under Filters, click on the Node: Type link, as shown by Item 1 in the following screenshot: As shown by Item 2 in the preceding screenshot, add Audio to the node types returned in this view. Click the Update button to store this change, and then click the Save button (not pictured in the preceding screenshot) to save the view. Editing the conversations View Click the Edit link for the conversations view. Then, in the Defaults display, under Arguments, click on the Search:Links to link, as shown by Item 1 in the following figure: As shown by Item 2 in the preceding screenshot, add Audio to the list of node types where this view will be validated. Click the Update button to store this change, and then click the Save button to save the view. As we add additional content types into the site, we will need to update these views to account for the newly-added content types.
Read more
  • 0
  • 0
  • 1849
Unlock access to the largest independent learning library in Tech for FREE!
Get unlimited access to 7500+ expert-authored eBooks and video courses covering every tech area you can think of.
Renews at €18.99/month. Cancel anytime
article-image-python-data-persistence-using-mysql-part-ii-moving-data-processing-data
Packt
27 Oct 2009
8 min read
Save for later

Python Data Persistence using MySQL Part II: Moving Data Processing to the Data

Packt
27 Oct 2009
8 min read
To move data processing to the data, you can use stored procedures, stored functions, and triggers. All these components are implemented inside the underlying database, and can significantly improve performance of your application due to reducing network overhead associated with multiple calls to the database. It is important to realize, though, the decision to move any piece of processing logic into the database should be taken with care. In some situations, this may be simply inefficient. For example, if you decide to move some logic dealing with the data stored in a custom Python list into the database, while still keeping that list implemented in your Python code, this can be inefficient in such a case, since it only increases the number of calls to the underlying database, thus causing significant network overhead. To fix this situation, you could move the list from Python into the database as well, implementing it as a table. Starting with version 5.0, MySQL supports stored procedures, stored functions, and triggers, making it possible for you to enjoy programming on the underlying database side. In this article, you will look at triggers in action. Stored procedures and functions can be used similarly. Planning Changes for the Sample Application Assuming you have followed the instructions in Python Data Persistence using MySQL, you should already have the application structure to be reorganized here. To recap, what you should already have is: tags nested list of tags used to describe the posts obtained from the Packt Book Feed page. obtainPost function obtains the information about the most recent post on the Packt Book Feed page. determineTags function determines tags appropriate to the latest post obtained from the Packt Book Feed page. insertPost function inserts the information about the obtained post into the underlying database tables: posts and posttags. execPr function brings together the functionality of the described above functions. That’s what you should already have on the Python side. And on the database side, you should have the following components: posts table contains records representing posts obtained from the Packt Book Feed page. posttags table contains records each of which represents a tag associated with a certain post stored in the posts table. Let’s figure out how we can refactor the above structure, moving some data processing inside the database. The first thing you might want to do is to move the tags list from Python into the database, creating a new table tags for that. Then, you can move the logic implemented with the determineTags function inside the database, defining the AFTER INSERT trigger on the posts table. From within this trigger, you will also insert rows into the posttags table, thus eliminating the need to do it from within the insertPost function. Once you’ve done all that, you can refactor the Python code implemented in the appsample module. To summarize, here are the steps you need to perform in order to refactor the sample application discussed in the earlier article: Create tags table and populate it with the data currently stored in the  tags list implemented in Python. Define the AFTER INSERT trigger on the posts table. Refactor the insertPost function in the appsample.py module. Remove the tags list from the appsample.py module. Remove the determineTags function from the appsample.py module. Refactor the execPr function in the appsample.py module. Refactoring the Underlying Database To keep things simple, the tags table might contain a single column tag with the primary key constraint defined on it. So, you can create the tags table as follows: CREATE TABLE tags ( tag VARCHAR(20) PRIMARY KEY ) ENGINE = InnoDB; Then, you might want to modify the posttags table, adding a foreign key constraint to its tag column. Before you can do that, though, you will need to delete all the rows from this table. This can be done with the following query: DELETE FROM posttags; Now you can move on and alter posttags as follows: ALTER TABLE posttags ADD FOREIGN KEY (tag) REFERENCES tags(tag); The next step is to populate the tags table. You can automate this process with the help of the following Python script: >>> import MySQLdb >>> import appsample >>> db=MySQLdb.connect(host="localhost",user="usrsample",passwd="pswd",db=">>> dbsample") >>> c=db.cursor() >>> c.executemany("""INSERT INTO tags VALUES(%s)""", appsample.tags) >>> db.commit() >>> db.close() As a result, you should have the tags table populated with the data taken from the tags list discussed in Python Data Persistence using MySQL. To make sure it has done so, you can turn back to the mysql prompt and issue the following query against the tags table: SELECT * FROM tags; The above should output the list of tags you have in the tags list. Of course, you can always extend this list, adding new tags with the INSERT statement. For example, you could issue the following statement to add the Visual Studio tag: INSERT INTO tags VALUES('Visual Studio'); Now you can move on and define the AFTER INSERT trigger on the posts table: delimiter // CREATE TRIGGER insertPost AFTER INSERT ON posts FOR EACH ROW BEGIN INSERT INTO posttags(title, tag) SELECT NEW.title as title, tag FROM tags WHERE LOCATE(tag, NEW.title)>0; END // delimiter ; As you can see, the posttags table will be automatically populated with appropriate tags just after a new row is inserted into the posts table. Notice the use of the INSERT … SELECT statement in the body of the trigger. Using this syntax lets you insert several rows into the posttags table at once, without having to use an explicit loop. In the WHERE clause of SELECT, you use standard MySQL string function LOCATE returning the position of the first occurrence of the substring, passed in as the first argument, in the string, passed in as the second argument. In this particular example, though, you are not really interested in obtaining the position of an occurrence of the substring in the string. All you need to find out here is whether the substring appears in the string or not. If it is, it should appear in the posttags table as a separate row associated with the row just inserted into the posts table. Refactoring the Sample’s Python Code Now that you have moved some data and data processing from Python into the underlying database, it’s time to reorganize the appsample custom Python module created as discussed in Python Data Persistence using MySQL. As mentioned earlier, you need to rewrite the insertPost and execPr functions and remove the determineTags function and the tags list. This is what the appsample module should look like after revising: import MySQLdb import urllib2 import xml.dom.minidom def obtainPost(): addr = "http://feeds.feedburner.com/packtpub/sDsa?format=xml" xmldoc = xml.dom.minidom.parseString(urllib2.urlopen(addr).read()) item = xmldoc.getElementsByTagName("item")[0] title = item.getElementsByTagName("title")[0].firstChild.data guid = item.getElementsByTagName("guid")[0].firstChild.data pubDate = item.getElementsByTagName("pubDate")[0].firstChild.data post ={"title": title, "guid": guid, "pubDate": pubDate} return post def insertPost(title, guid, pubDate): db=MySQLdb.connect(host="localhost",user="usrsample",passwd="pswd",db="dbsample") c=db.cursor() c.execute("""INSERT INTO posts (title, guid, pubDate) VALUES(%s,%s,%s)""", (title, guid, pubDate)) db.commit() db.close() def execPr(): p = obtainPost() insertPost(p["title"], p["guid"], p["pubDate"]) If you compare it with appsample discussed in Part 1, you should notice that the revision is much shorter. It’s important to note, however, that nothing has changed from the user standpoint. So, if you now start the execPr function in your Python session: >>>import appsample >>>appsample.execPr() This should insert a new record into the posts table, inserting automatically corresponding tags records into the posttags table, if any. The difference lies in the way it’s going on behind the scenes. Now the Python code is responsible only for obtaining the latest post from the Packt Book Feed page and then inserting a record into the posts table. Dealing with tags is now responsibility of the logic implemented inside the database. In particular, the AFTER INSERT trigger defined on the posts table should take care of inserting the rows into the posttags table. To make sure that everything has worked smoothly, you can now check out the content of the posts and posttags tables. To look at the latest post stored in the posts table, you could issue the following query: SELECT title, str_to_date(pubDate,'%a, %e %b %Y') lastdate FROM posts ORDER BY lastdate DESC LIMIT 1; Then, you might want to look at the related tags stored in the posttags tables, by issuing the following query: SELECT p.title, t.tag, str_to_date(p.pubDate,'%a, %e %b %Y') lastdate FROM posts p, posttags t WHERE p.title=t.title ORDER BY lastdate DESC LIMIT 1; Conclusion In this article, you looked at how some business logic of a Python/MySQL application can be moved from Python into MySQL. For that, you continued with the sample application originally discussed in Python Data Persistence using MySQL.
Read more
  • 0
  • 0
  • 4620

article-image-managing-student-work-using-moodle-part-3
Packt
27 Oct 2009
5 min read
Save for later

Managing Student Work using Moodle: Part 3

Packt
27 Oct 2009
5 min read
Specifying Custom Grades Currently, I'm marking my projects out of 100 but, as I mentioned previously, that's not how they are graded. According to the syllabus, I can only give students one of four grades: Distinction, Merit, Pass, and Referral. So how do you specify your own grades? Let's learn how to do that now. Time for Action – Create a Custom Grade Scale Return to your course front page and look for Grades in the Administration block: Click on Grades and you'll be taken to the Grader report page. We are now in the Moodle grade book. I'm not going to worry too much about all of the features in the grade book for the moment—but while you are there you might like to spend a little time having a look. As with anything else in Moodle, you can't do any damage by doing something by mistake. At the top left of the page, you will find a list of view options: From the list select Scales. You're now taken to the scales page. We need to add a new scale, so press the Add a new scale button in the center of the page. On the following page, give your new scale a name and in the Scale box you can specify the possible grades contained in your new scale. Separate the grades with commas—no spaces. Make sure you specify the grades in order of increasing value: You don't have to worry about a description. Are the grades you are specifying here used for grading in other courses? If you tick the Standard scale box then your scale will be made available to teachers on all courses. When you are done, press the Save changes button. Your new scale is listed on the scales page. Because I didn't make my new scale a standard scale, it's listed as a custom scale: What Just Happened? I don't give students a numerical grade for the Backyard Ballistics projects. The syllabus requires a qualitative grade, but luckily the system makes it easy to import my own custom grade scales. All I need to do now is modify my two assignment activities to use the new scale. That only involves a few clicks, so let's do that now... Time for Action – Grading Using a Custom Scale Return to your course front page and click on the update icon next to the assignment you want to change to use your new custom scale. The Editing assignment page is displayed. Scroll down to the Grade drop-down list. Click on the list. Scroll up if you need to, because the custom scale we want to use will be towards the very top: With the new grading scale selected, scroll down to the bottom of the page and press the Save and return to course button. That's it. You will now be able to grade your project using your new scale. What Just Happened? We've just modified the assignment to use our new grading scale. All that remains now is to demonstrate how you use it. Now that we are back at the course front page, click on the link to the assignment itself to display the assignment's main page (displaying the description of the task we've set). Click on the View submitted assignments link in the top right-hand corner of the page to take you to the Submissions page. Choose a student and down in the Status column click on the Grade link. If you've already marked that student then the link will say Update: Click on the link to open the Feedback dialog. Click on the Grade list at the top right-hand corner of the page to display the grades you can give to this piece of work. The grades listed are the ones from our new custom grade scale: More Uses for Moodle Assignments We aren't limited to using the four assignment activities just for major projects. Here are some more ideas on using the assignment activity to convert your current teaching over to Moodle... Include an online text assignment for discursive tasks, for example writing a short story or for short essay homework tasks. If you're able to display the submissions page of a single file assignment to the class during teaching time, keep refreshing the page as homework is submitted. You'll quickly find that there'll be a race on to be the first to hand their homework in. You could easily turn that into a game for younger students. Use an Offline activity to manage the grades of any task you set for your students—homework handed in on paper, for example. You don't have to confine yourself to just projects. On that last point, there is another way of managing grades directly. We've already been briefly into the Moodle Grader report when we set up our custom scale. Let's revisit that page to see how we can set up custom grading items. Grading Students on Core Competencies Often, as educators, we need to grade assignments on core competencies, otherwise known as key skills or goals. That certainly applies to my syllabus: A percentage of the final grade for my course includes marks for numeracy, literacy, and the use of ICT. Because we are converting to Moodle, and in Moodle-speak, the competencies that I am grading are called "outcomes", in this final section, we learn how to specify the core competencies we need to grade, and how we can then grade students on them. There are pros and cons of converting to Moodle, specifically: I can choose to enable outcomes on a per assignment basis, but you can't use the default numeric grading scale to grade outcomes, only standard and custom grading scales (like my custom Backyard Ballistics scale that I created in Time for action – Create a Custom Grade Scale).
Read more
  • 0
  • 0
  • 1537

article-image-implementing-basic-helloworld-wcf-windows-communication-foundation-service
Packt
27 Oct 2009
7 min read
Save for later

Implementing a Basic HelloWorld WCF (Windows Communication Foundation) Service

Packt
27 Oct 2009
7 min read
We will build a HelloWorld WCF service by carrying out the following steps: Create the solution and project Create the WCF service contract interface Implement the WCF service Host the WCF service in the ASP.NET Development Server Create a client application to consume this WCF service Creating the HelloWorld solution and project Before we can build the WCF service, we need to create a solution for our service projects. We also need a directory in which to save all the files. Throughout this article, we will save our project source codes in the D:SOAwithWCFandLINQProjects directory. We will have a subfolder for each solution we create, and under this solution folder, we will have one subfolder for each project. For this HelloWorld solution, the final directory structure is shown in the following image: You don't need to manually create these directories via Windows Explorer; Visual Studio will create them automatically when you create the solutions and projects. Now, follow these steps to create our first solution and the HelloWorld project: Start Visual Studio 2008. If the Open Project dialog box pops up, click Cancel to close it. Go to menu File | New | Project. The New Project dialog window will appear. From the left-hand side of the window (Project types), expand Other Project Types and then select Visual Studio Solutions as the project type. From the right-hand side of the window (Templates), select Blank Solution as the template. At the bottom of the window, type HelloWorld as the Name, and D:SOAwithWCFandLINQProjects as the Location. Note that you should not enter HelloWorld within the location, because Visual Studio will automatically create a folder for a new solution. Click the OK button to close this window and your screen should look like the following image, with an empty solution. Depending on your settings, the layout may be different. But you should still have an empty solution in your Solution Explorer. If you don't see Solution Explorer, go to menu View | Solution Explorer, or press Ctrl+Alt+L to bring it up. In the Solution Explorer, right-click on the solution, and select Add | New Project… from the context menu. You can also go to menu File | Add | New Project… to get the same result. The following image shows the context menu for adding a new project. The Add New Project window should now appear on your screen. In the left-hand side of this window (Project types), select Visual C# as the project type, and on the right-hand side of the window (Templates), select Class Library as the template. At the bottom of the window, type HelloWorldService as the Name. Leave D:SOAwithWCFandLINQProjectsHelloWorld as the Location. Again, don't add HelloWorldService to the location, as Visual Studio will create a subfolder for this new project (Visual Studio will use the solution folder as the default base folder for all the new projects added to the solution). You may have noticed that there is already a template for WCF Service Application in Visual Studio 2008. For the very first example, we will not use this template. Instead, we will create everything by ourselves so you know what the purpose of each template is. This is an excellent way for you to understand and master this new technology. Now, you can click the OK button to close this window. Once you click the OK button, Visual Studio will create several files for you. The first file is the project file. This is an XML file under the project directory, and it is called HelloWorldService.csproj. Visual Studio also creates an empty class file, called Class1.cs. Later, we will change this default name to a more meaningful one, and change its namespace to our own one. Three directories are created automatically under the project folder—one to hold the binary files, another to hold the object files, and a third one for the properties files of the project. The window on your screen should now look like the following image: We now have a new solution and project created. Next, we will develop and build this service. But before we go any further, we need to do two things to this project: Click the Show All Files button on the Solution Explorer toolbar. It is the second button from the left, just above the word Solution inside the Solution Explorer. If you allow your mouse to hover above this button, you will see the hint Show All Files, as shown in above diagram. Clicking this button will show all files and directories in your hard disk under the project folder-rven those items that are not included in the project. Make sure that you don't have the solution item selected. Otherwise, you can't see the Show All Files button. Change the default namespace of the project. From the Solution Explorer, right-click on the HelloWorldService project, select Properties from the context menu, or go to menu item Project | HelloWorldService Properties…. You will see the project properties dialog window. On the Application tab, change the Default namespace to MyWCFServices. Lastly, in order to develop a WCF service, we need to add a reference to the ServiceModel namespace. On the Solution Explorer window, right-click on the HelloWorldService project, and select Add Reference… from the context menu. You can also go to the menu item Project | Add Reference… to do this. The Add Reference dialog window should appear on your screen. Select System.ServiceModel from the .NET tab, and click OK. Now, on the Solution Explorer, if you expand the references of the HelloWorldService project, you will see that System.ServiceModel has been added. Also note that System.Xml.Linq is added by default. We will use this later when we query a database. Creating the HelloWorldService service contract interface In the previous section, we created the solution and the project for the HelloWorld WCF Service. From this section on, we will start building the HelloWorld WCF service. First, we need to create the service contract interface. In the Solution Explorer, right-click on the HelloWorldService project, and select Add | New Item…. from the context menu. The following Add New Item - HelloWorldService dialog window should appear on your screen. On the left-hand side of the window (Categories), select Visual C# Items as the category, and on the right-hand side of the window (Templates), select Interface as the template. At the bottom of the window, change the Name from Interface1.cs to IHelloWorldService.cs. Click the Add button. Now, an empty service interface file has been added to the project. Follow the steps below to customize it. Add a using statement: using System.ServiceModel; Add a ServiceContract attribute to the interface. This will designate the interface as a WCF service contract interface. [ServiceContract] Add a GetMessage method to the interface. This method will take a string as the input, and return another string as the result. It also has an attribute, OperationContract. [OperationContract] String GetMessage(String name); Change the interface to public. The final content of the file IHelloWorldService.cs should look like the following: using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.ServiceModel;namespace MyWCFServices{[ServiceContract]public interface IHelloWorldService{[OperationContract]String GetMessage(String name);}}
Read more
  • 0
  • 0
  • 2748

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

Real Content in PHP5 CMS: Part 1

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

Oracle Web RowSet - Part2

Packt
27 Oct 2009
4 min read
Reading a Row Next, we will read a row from the OracleWebRowSet object. Click on Modify Web RowSet link in the CreateRow.jsp. In the ModifyWebRowSet JSP click on the Read Row link. The ReadRow.jsp JSP is displayed. In the ReadRow JSP specify the Database Row to Read and click on Apply. The second row values are retrieved from the Web RowSet: In the ReadRow JSP the readRow() method of the WebRowSetQuery.java application is invoked. TheWebRowSetQuery object is retrieved from the session object. WebRowSetQuery query=( webrowset.WebRowSetQuery)session.getAttribute("query"); The String[] values returned by the readRow() method are added to theReadRow JSP fields. In the readRow() method theOracleWebRowSet object cursor is moved to the row to be read. webRowSet.absolute(rowRead); Retrieve the row values with the getString() method and add to String[]. Return the String[] object. String[] resultSet=new String[5];resultSet[0]=webRowSet.getString(1);resultSet[1]=webRowSet.getString(2);resultSet[2]=webRowSet.getString(3);resultSet[3]=webRowSet.getString(4);resultSet[4]=webRowSet.getString(5);return resultSet; ReadRow.jsp JSP is listed as follows: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN""http://www.w3.org/TR/html4/loose.dtd"><%@ page contentType="text/html;charset=windows-1252"%><%@ page session="true"%><html><head><meta http-equiv="Content-Type" content="text/html;charset=windows-1252"><title>Read Row with Web RowSet</title></head><body><form><h3>Read Row with Web RowSet</h3><table><tr><td><a href="ModifyWebRowSet.jsp">Modify Web RowSetPage</a></td></tr></table></form><%webrowset.WebRowSetQuery query=null;query=( webrowset.WebRowSetQuery)session.getAttribute("query");String rowRead=request.getParameter("rowRead");String journalUpdate=request.getParameter("journalUpdate");String publisherUpdate=request.getParameter("publisherUpdate");String editionUpdate=request.getParameter("editionUpdate");String titleUpdate=request.getParameter("titleUpdate");String authorUpdate=request.getParameter("authorUpdate");if((rowRead!=null)){int row_Read=Integer.parseInt(rowRead);String[] resultSet=query.readRow(row_Read);journalUpdate=resultSet[0];publisherUpdate=resultSet[1];editionUpdate=resultSet[2];titleUpdate=resultSet[3];authorUpdate=resultSet[4];}%><form name="query" action="ReadRow.jsp" method="post"><table><tr><td>Database Row to Read:</td></tr><tr><td><input name="rowRead" type="text" size="25"maxlength="50"/></td></tr><tr><td>Journal:</td></tr><tr><td><input name="journalUpdate" value='<%=journalUpdate%>'type="text" size="50" maxlength="250"/></td></tr><tr><td>Publisher:</td></tr><tr><td><input name="publisherUpdate"value='<%=publisherUpdate%>' type="text" size="50"maxlength="250"/></td></tr><tr><td>Edition:</td></tr><tr><td><input name="editionUpdate" value='<%=editionUpdate%>'type="text" size="50" maxlength="250"/></td></tr><tr><td>Title:</td></tr><tr><td><input name="titleUpdate" value='<%=titleUpdate%>'type="text" size="50" maxlength="250"/></td></tr><tr><td>Author:</td></tr><tr><td><input name="authorUpdate" value='<%=authorUpdate%>'type="text" size="50" maxlength="250"/></td></tr><tr><td><input class="Submit" type="submit" value="Apply"/></td></tr></table></form></body></html>
Read more
  • 0
  • 0
  • 1587

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

Real Content in PHP5 CMS: Part 3

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

article-image-creating-dialplan-asterisk-16-part-2
Packt
27 Oct 2009
14 min read
Save for later

Creating a Dialplan in Asterisk 1.6: Part 2

Packt
27 Oct 2009
14 min read
Advanced Call Distribution What exactly is Advanced Call Distribution ? Many phone systems tout this feature, but most do not adequately define what it means. Basically, it refers to using call queues, parking calls for another user to answer, and Direct Inward Dialing (DID). So that we keep our focus, we will look at each of these elements individually. Call queues We have already configured call queues through the /etc/asterisk/queues.conf file. As we go through how we're going to use our queues, we may decide we want to change the way our queues are configured. There is absolutely no problem with changing the configuration so that it more accurately reflects our needs. Just remember that we need to issue a reload on the Asterisk console, or type #asterisk –r –x reload at the command line. The power and flexibility of other ACD systems can be matched or exceeded by Asterisk. As we evaluate our needs, we should remember that configuring a single aspect of Asterisk sometimes requires changes to more than one file. For example, queues will be configured both in the queues.conf file and the extensions.conf file. We will discuss how to set up extensions.conf to give us the desired result. When dealing with call queues, we need to think about the two types of users we have. First, we have the caller who calls in and waits in the queue for the next agent. We can think of this person as our customer. Next, we have the agents who work the queue. We can think of these people as our users. As a business, we have to decide what we want our customers' experience to be. Our call queue can make it sound like a phone is ringing. Or we can use music on hold while the customer waits. We can also announce call position and estimated wait time if we want to. When we place customers in a queue, we use the Queue application. To place a caller in the queue named bob, we would use something like: exten => 1000,1,Queue(bob) Suppose we have an operator's extension. As Ollie the operator may have more than one call at a time, we decide to give him a call queue. His calls are always about a minute long. The customers waiting for him are going to be there because they got lost in a system of menus. His queue will be named operator. In this instance, we will choose to have the customer hear the ring, so they will believe they are about to be helped. The sound of ringing should not last more than about a minute. We will not announce call queue length because our customer should not know that he or she is in a queue. The entry for this queue would be: exten => 0,1,Queue(operator|tr) Notice our use of options. Options for the queue application include: t: Allow the user to transfer the customer. T: Allow the customer to transfer the user. d: This is a data-quality call. H: Allow the customer to hang up by hitting *. n: Do not retry on timeout. The next step in the dialplan will be executed. r: Give the customer the ringing sound instead of music on hold. Thus, we told the Queue application to make the customer hear the ring, and the user (Ollie) the ability to transfer calls (as he's the operator). Now, suppose we have Rebecca, the receptionist at SIP phone 1006. When Ollie goes to the bathroom, we want our poor lost customers to be routed to her. So we could use the following in our extensions.conf file: exten => 0,1,Queue(operator|trn)exten => 0,2,Dial(SIP/1006) Now, Rebecca had better answer this. Until she does, the phone will continue to ring. Notice that this call will never end up in Rebecca's voicemail, as it is not transferred to her extension, but instead dials her phone directly. We have adequately addressed the customer's experience. But now we need to look at how our users will join and leave the queue. Previously, we discussed the power and flexibility of using agents in queues. As with most things in Asterisk, there are many ways we can associate members to queues. The three main ways are—statically, dynamically, and by using agents. Our first option is to have members statically assigned to the queue. In order to do this, we use the member directive in the queues.conf file. This is most helpful when we have a queue with fixed members, such as a switchboard queue. Our second option is to allow members to log in dynamically. We do this through the AddQueueMember application. An example of this would be: exten => 8101,1,AddQueueMember(myqueue|SIP/1001) Whenever anybody dials extension 8101, the telephone handset SIP/1001 would be added to the queue named myqueue. All that we would have to do is define a login extension for every member of every queue. What happens when this member no longer wishes to be in the queue? We use the RemoveQueueMember application, like this: exten => 8201,1,RemoveQueueMember(myqueue|SIP/1001) With this configuration, whenever anybody dials extension 8201, the telephone handset at SIP/1001 is removed. Again, we would have to define a logout extension for each member of the queue. Suppose we did not wish to define a login and logout extension for each member. We have the option of leaving off the interface (SIP/1001 in the previous example) and having Asterisk use our current extension. While this is very useful, Asterisk does not always use the right value. However, if it works for all extensions that need to be in the queue, we would only have to define one login and one logout per queue. The code would look like: exten => 8101,1,AddQueueMember(myqueue)exten => 8201,1,RemoveQueueMember(myqueue) This is better than having to define a login and logout for each member of each queue, but sometimes users are not good at remembering multiple extensions to dial. The AddQueueMember application will jump to priority n+101 if that interface is already a member of the queue. Therefore, we could define an extension like: exten => 8101,1,Answerexten => 8101,2,AddQueueMember(myqueue)exten => 8101,3,Playback(agent-loginok)exten => 8101,4,Hangupexten => 8101,103,RemoveQueueMember(myqueue)exten => 8101,102,Playback(agent-loggedoff)exten => 8101,105,Hangup When we define it this way, a user dialing extension 8101 is logged in if not already a member of the queue, or logged out if in the queue. Also, we added a confirmation to the action, so that the user can know if they are now in or out of the queue. Notice that before we could use the Playback application, we had to answer the call. If we have a lot of these, we could define a macro extension, like: [macro-queueloginout]exten => s,1,Answerexten => s,2,AddQueueMember(${ARG1})exten => s,3,Playback(agent-loginok)exten => s,4,Hangupexten => s,103,RemoveQueueMember(${ARG1})exten => s,104,Playback(agent-loggedoff)exten => s,105,Hangup. . .[default]exten => 8101,1,Macro(queueloginout|queue1)exten => 8102,1,Macro(queueloginout|queue2)exten => 8103,1,Macro(queueloginout|queue3) And thus we see that using a macro will save us five lines in our extensions.conf for every queue after the first. This is how we can add queue members dynamically. Our final option for adding queue members is by using Asterisk's agent settings. We were able to define agents in /etc/asterisk/agents.conf. We create an agent by defining an ID and a password, and listing the agent's name. In the queues.conf, we could define agents as members of queues. Calls will not be sent to agents unless they are logged in. In this way, queues can be both dynamic and static—they are static when we do not change the members of the queues, but dynamic when calls will go to different handsets based upon which agents are logged in. There are two main types of agents in this world. There are the archetypical large call center agents who work with a headset and never hear rings, and there are the lower-volume agents whose phone rings each time a call comes in. Asterisk has the flexibility to handle both types of agents, even in the same queue. First, imagine a huge call center that takes millions of phone calls per day. Each agent is in multiple queues, and we have set each queue to use an announcement at the beginning of calls to let the agent know which queue the call is coming in from. As employees arrive for their shift, they sit down at an empty station, plug in their headset, and log in. Each employee will hear music in between calls, and then hear a beep, and the call will be connected. To accomplish this, we use the line: exten => 8001,1,AgentLogin Through the normal login, the call is kept active the whole time. The agents will logout by hanging up the phone. This allows large call centers to be quieter, as the distraction of ringing phones will be removed. It also allows for more efficient answering of lines, as the time required to pick up the phone is eliminated. When our users arrive at work and wish to log in, they call extension 8001, where they are prompted for their agent ID, password, and then an extension number at which they will take calls. This is how Asterisk knows how to reach them. Our agents can log out when using AgentCallbackLogin by going through the same procedure as for login, with the exception that when they are prompted for their extension, they press the # key. It may be a good idea for us to review agents.conf. If we defined autologoff, then after the specified number of seconds of ringing, the agent will be automatically logged off. If we set ackcall to yes, then agents must press the # key to accept calls. If we created a wrapuptime (defined in milliseconds), then Asterisk will wait that many milliseconds before sending another call to the agent. These options can help us make our phone system as user friendly as we want it to be. Through the use of call queues, we can distribute our incoming calls efficiently and effectively. We have plenty of options, and can mix and match these three ways of joining users to queues. Call parking In many businesses across the United States, an operator can be heard announcing "John, you have a call on line 3. John, line 3." In Asterisk, we don't really have lines the way analog PBXs do. Our users are accustomed to not having to transfer calls, especially when they may not know exactly where John is. Asterisk uses a feature known as call parking to accomplish this same goal. Our users will transfer calls to a special extension, which will then tell them what extension to call in order to retrieve the call. Then our users can direct the intended recipient to dial that extension and connect to the call. In order to be able to use this feature, we must define our parking lot. This is done in the /etc/asterisk/parking.conf file. In this file, there are only a few options that we will need to configure. First, we must create the extension that people are to dial in order to park calls. This can be whatever extension is convenient for us. Then we will define a list of extensions on which to place parked calls. These extensions will be what users dial to retrieve a parked call. Next, we will define what context we want our parked calls to be in. Finally, we will define how many seconds a call remains parked before ringing back to the user who parked it. Here is an example: [general]parkext => 8100parkpos => 8101-8199context => parkedcallsparkingtime => 120 These settings would mean that we can park calls by dialing 8100, and the call will be placed in extensions 8101 through 8199, giving us the ability to have up to 99 parked calls at any given time. The calls will be in the context called parkedcalls, which means we should be careful to include it in any context where users should be able to park and retrieve calls. When our users transfer a call to extension 8100, they will hear Asterisk read out the extension that the call has been placed on. They can now make a note of it and notify the appropriate co-worker of the extension to reach the calling customer on. If the call is not picked up within the given parkingtime, then the call will ring back to the user who parked the call. By using call parking, we can help our users by providing a feature similar to that of previous generations of PBXs. This also allows users to collaborate and redirect callers to other users who are better equipped to handle our customers' needs. Direct Inward Dialing (DID) Suppose we work at a healthcare company with over 100 employees. We have two PRI lines coming in, and only three switchboard agents to handle incoming calls. As a healthcare company, we schedule many appointments, answer questions about prescriptions, and help patients with billing questions. These three agents are always busy. Now suppose the IT guy's wife calls in to ask if he wants sprouts or mash with his dinner. Do we want our switchboard agents to have to answer the call, find out who it is and what they want, and then transfer the call, or would we rather want the IT guy's wife to call her husband directly? This is where Direct Inward Dialing (DID) comes in handy. DID is a service provided by phone companies where they send an agreed-upon set of digits, depending on the number the customer dialed. For most phone companies, the sent digits will be the full ten-digit number (in the United States). But this can be as small as the last digit. All right, so the phone company is sending digits. What are we going to do with them? Imagine you have a PRI coming in to your office, and only ten phone numbers—a block from (850) 555-5550 to 5559. Your phone company has agreed to send you only the last digit dialed, which will be from 0 to 9, because you are guaranteed for this to be unique. Asterisk can route calls based on this DID information. If we have our PRI line's channels defined to go into a context called incoming, this context could look like: [incoming]s,1,Goto(default,s,1)i,1,Goto(default,s,1)t,1,Goto(default,s,1)0,1,Goto(default,1234,1)1,1,Goto(default,2345,1)2,1,Goto(default,3456,1)3,1,Goto(default,4567,1)4,1,Goto(default,5678,1)5,1,Goto(default,6789,1)6,1,Goto(default,7890,1)7,1,Goto(default,1111,1)8,1,Goto(default,1111,1)9,1,Goto(default,1111,1) There are a few things we should notice about this. First, we handled the error cases. What if a glitch at the phone company results in four digits being sent? We cannot allow a simple mistake on their end to interrupt our ability to receive phone calls. Secondly, we are using Goto statements. We've briefly discussed how they can be both good and bad. In this case, if a user moves from one extension to another by using Goto, we have to update it only in the default context. Finally, we are allowed to send multiple incoming DIDs to the same extension, if we so desire, as in the last three lines shown in the previous code. This might be useful if extension 1111 is the operator, and we do not yet have the number 7, 8, or 9 assigned to a user. Of course, in real life this is going to get much more complicated, as phone numbers will probably come in with the full ten digits. But the concept is the same—we can define extensions based upon information that the phone company sends when the call is established. By using DIDs, we can cut down on bottlenecks and give direct access to certain extensions. This tool of Asterisk helps make our phone system fast, efficient, and friendly to our users and customers.  
Read more
  • 0
  • 0
  • 3261
article-image-creating-shopping-cart-using-zend-framework-part-1
Packt
27 Oct 2009
13 min read
Save for later

Creating a Shopping Cart using Zend Framework: Part 1

Packt
27 Oct 2009
13 min read
Our next task in creating the storefront is to create the shopping cart. This will allow users to select the products they wish to purchase. Users will be able to select, edit, and delete items from their shopping cart. Lets get started. Creating the Cart Model and Resources We will start by creating our model and model resources. The Cart Model differs from our previous model in the fact that it will use the session to store its data instead of the database. Cart Model The Cart Model will store the products that they wish to purchase. Therefore, the Cart Model will contain Cart Items that will be stored in the session. Let's create this class now. application/modules/storefront/models/Cart.php class Storefront_Model_Cart extends SF_Model_Abstract implements SeekableIterator, Countable, ArrayAccess { protected $_items = array(); protected $_subTotal = 0; protected $_total = 0; protected $_shipping = 0; protected $_sessionNamespace; public function init() { $this->loadSession(); } public function addItem( Storefront_Resource_Product_Item_Interface $product,$qty) { if (0 > $qty) { return false; } if (0 == $qty) { $this->removeItem($product); return false; } $item = new Storefront_Resource_Cart_Item( $product, $qty ); $this->_items[$item->productId] = $item; $this->persist(); return $item; } public function removeItem($product) { if (is_int($product)) { unset($this->_items[$product]); } if ($product instanceof Storefront_Resource_Product_Item_Interface) { unset($this->_items[$product->productId]); } $this->persist(); } public function setSessionNs(Zend_Session_Namespace $ns) { $this->_sessionNamespace = $ns; } public function getSessionNs() { if (null === $this->_sessionNamespace) { $this->setSessionNs(new Zend_Session_Namespace(__CLASS__)); } return $this->_sessionNamespace; } public function persist() { $this->getSessionNs()->items = $this->_items; $this->getSessionNs()->shipping = $this->getShippingCost(); } public function loadSession() { if (isset($this->getSessionNs()->items)) { $this->_items = $this->getSessionNs()->items; } if (isset($this->getSessionNs()->shipping)) { $this->setShippingCost($this->getSessionNs()->shipping); } } public function CalculateTotals() { $sub = 0; foreach ($this as $item) { $sub = $sub + $item->getLineCost(); } $this->_subTotal = $sub; $this->_total = $this->_subTotal + (float) $this->_shipping; } public function setShippingCost($cost) { $this->_shipping = $cost; $this->CalculateTotals(); $this->persist(); } public function getShippingCost() { $this->CalculateTotals(); return $this->_shipping; } public function getSubTotal() { $this->CalculateTotals(); return $this->_subTotal; } public function getTotal() { $this->CalculateTotals(); return $this->_total; } /*...*/ } We can see that the Cart Model class is fairly weighty and in fact, we have not included the full class here. The reason we have slightly truncated the class is that we are implementing the SeekableIterator, Countable, and ArrayAccess interfaces. These interfaces are defined by PHP's SPL Library and we use them to provide a better way to interact with the cart data. For the complete code, copy the methods below getTotal() from the example files for this article. We will look at what each method does shortly in the Cart Model implementation section, but first, let's look at what functionality the SPL interfaces allow us to add. Cart Model interfaces The SeekableIterator interface allows us to access our cart data in these ways: // iterate over the cartforeach($cart as $item) {}// seek an item at a position$cart->seek(1);// standard iterator access$cart->rewind();$cart->next();$cart->current(); The Countable interface allows us to count the items in our cart: count($cart); The ArrayAccess interface allows us to access our cart like an array: $cart[0]; Obviously, the interfaces provide no concrete implementation for the functionality, so we have to provide it on our own. The methods not listed in the previous code listing are: offsetExists($key) offsetGet($key) offsetSet($key, $value) offsetUnset($key) current() key() next() rewind() valid() seek($index) count() We will not cover the actual implementation of these interfaces, as they are standard to PHP. However, you will need to copy all these methods from the example files to get the Cart Model working. Documentation for the SPL library can be found athttp://www.php.net/~helly/php/ext/spl/     Cart Model implementation Going back to our code listing, let's now look at how the Cart Model is implemented. Let's start by looking at the properties and methods of the class. The Cart Model has the following class properties: $_items:An array of cart items $_subTotal: Monetary total of cart items $_total: Monetary total of cart items plus shipping $_shipping: The shipping cost $_sessionNamespace: The session store The Cart Model has the following methods: init(): Called during construct and loads the session data addItem(Storefront_Resource_Product_Item_Interface $product, $qty): Adds or updates items in the cart removeItem($product): Removes a cart item setSessionNs(Zend_Session_Namespace $ns): Sets the session instance to use for storage getSessionNs(): Gets the current session instance persist(): Saves the cart data to the session loadSession(): Loads the stored session values calculateTotals(): Calculates the cart totals setShippingCost($cost): Sets the shipping cost getShippingCost(): Gets the shipping cost getSubTotal(): Gets the total cost for items in the cart (not including the shipping) getTotal(): Gets the subtotal plus the shipping cost When we instantiate a new Cart Model instance, the init() method is called. This is defined in the SF_Model_Abstract class and is called by the __construct() method. This enables us to easily extend the class's instantiation process without having to override the constructor. The init() method simply calls the loadSession() method. This method populates the model with the cart items and shipping information stored in the session. The Cart Model uses Zend_Session_Namespace to store this data, which provides an easy-to-use interface to the $_SESSION variable. If we look at the loadSession() method, we see that it tests whether the items and shipping properties are set in the session namespace. If they are, then we set these values on the Cart Model. To get the session namespace, we use the getSessionNs() method. This method checks if the $_sessionNs property is set and returns it. Otherwise it will lazy load a new Zend_Session_Namespace instance for us. When using Zend_Session_ Namespace, we must provide a string to its constructor that defines the name of the namespace to store our data in. This will then create a clean place to add variables to, without worrying about variable name clashes. For the Cart Model, the default namespace will be Storefront_Model_Cart. The Zend_Session_Namespace component provides a range of functionality that we can use to control the session. For example, we can set the expiration time as follows: $ns = new Zend_Session_Namespace('test');$ns->setExpirationSeconds(60, 'items');$ns->setExpirationHops(10);$ns->setExpirationSeconds(120); This code would set the item's property expiration to 60 seconds and the namespaces expiration to 10 hops (requests) or 120 seconds, whichever is reached first. The useful thing about this is that the expiration is not global. Therefore, we can have specialized expiration per session namespace. There is a full list of Zend_Session_Namespace functionalities in the reference manual. Testing with Zend_Session and Zend_Session_NamespaceTesting with the session components can be fairly diffi cult. For the Cart Model, we use the setSessionNs() method to allow us to inject a mock object for testing, which you can see in the Cart Model unit tests. There are plans to rewrite the session components to make testing easier in the future, so keep an eye out for those updates. To add an item to the cart, we use the addItem() method. This method accepts two parameters,$product and $qty. The $product parameter must be an instance of the Storefront_Resource_Product_Item class, and the $qty parameter must be an integer that defines the quantity that the customer wants to order. If the addItem() method receives a valid $qty, then it will create a new Storefront_Resource_Cart_Item and add it to the $_items array using the productId as the array key. We then call the persist() method. This method simply stores all the relevant cart data in the session namespace for us. You will notice that we are not using a Model Resource in the Cart Model and instead we are directly instantiating a Model Resource Item. This is because the Model Resources represent store items and the Cart Model is already doing this for us so it is not needed. To remove an item, we use the removeItem() method. This accepts a single parameter $product which can be either an integer or a Storefront_Resource_Product_Item instance. The matching cart item will be removed from the $_items array and the data will be saved to the session. Also, addItem() will call removeItem() if the quantity is set to zero. The other methods in the Cart Model are used to calculate the monetary totals for the cart and to set the shipping. We will not cover these in detail here as they are fairly simple mathematical calculations. Cart Model Resources Now that we have our Model created, let's create the Resource Interface and concrete Resource class for our Model to use. application/modules/storefront/models/resources/Cart/Item/Interface.php interface Storefront_Resource_Cart_Item_Interface { public function getLineCost(); } The Cart Resource Item has a very simple interface that has one method, getLineCost(). This method is used when calculating the cart totals in the Cart Model. application/modules/storefront/models/resources/Cart/Item.php class Storefront_Resource_Cart_Item implements Storefront_Resource_Cart_Item_Interface { public $productId; public $name; public $price; public $taxable; public $discountPercent; public $qty; public function __construct(Storefront_Resource_Product_Item_ Interface $product, $qty) { $this->productId = (int) $product->productId; $this->name = $product->name; $this->price = (float) $product->getPrice(false,false); $this->taxable = $product->taxable; $this->discountPercent = (int) $product->discountPercent; $this->qty = (int) $qty; } public function getLineCost() { $price = $this->price; if (0 !== $this->discountPercent) { $discounted = ($price*$this->discountPercent)/100; $price = round($price - $discounted, 2); } if ('Yes' === $this->taxable) { $taxService = new Storefront_Service_Taxation(); $price = $taxService->addTax($price); } return $price * $this->qty; } } The concrete Cart Resource Item has two methods __construct() and getLineCost(). The constructor accepts two parameters $product and $qty that must be a Storefront_Resource_Product_Item_Interface instance and integer respectively. This method will then simply copy the values from the product instance and store them in the matching public properties. We do this because we do not want to simply store the product instance because it has all the database connection data contained within. This object will be serialized and stored in the session. The getLineCost() method simply calculates the cost of the product adding tax and discounts and then multiplies it by the given quantity. Shipping Model We also need to create a Shipping Model so that the user can select what type of shipping they would like. This Model will simply act as a data store for some predefined shipping values. application/modules/storefront/models/Shipping.php class Storefront_Model_Shipping extends SF_Model_Abstract { protected $_shippingData = array( 'Standard' => 1.99, 'Special' => 5.99, ); public function getShippingOptions() { return $this->_shippingData; } } The shipping Model is very simple and only contains the shipping options and a single method to retrieve them. In a normal application, shipping would usually be stored in the database and most likely have its own set of business rules. For the Storefront, we are not creating a complete ordering process so we do not need these complications. Creating the Cart Controller With our Model and Model Resources created, we can now start wiring the application layer together. The Cart will have a single Controller, CartController that will be used to add, view, and update cart items stored in the Cart Model. application/modules/storefront/controllers/CartController.php class Storefront_CartController extends Zend_Controller_Action { protected $_cartModel; protected $_catalogModel; public function init() { $this->_cartModel = new Storefront_Model_Cart(); $this->_catalogModel = new Storefront_Model_Catalog(); } public function addAction() { $product = $this->_catalogModel->getProductById( $this->_getParam('productId') ); if(null === $product) { throw new SF_Exception('Product could not be added to cart as it does not exist' ); } $this->_cartModel->addItem( $product, $this->_getParam('qty') ); $return = rtrim( $this->getRequest()->getBaseUrl(), '/' ) . $this->_getParam('returnto'); $redirector = $this->getHelper('redirector'); return $redirector->gotoUrl($return); } public function viewAction() { $this->view->cartModel = $this->_cartModel; } public function updateAction() { foreach($this->_getParam('quantity') as $id => $value) { $product = $this->_catalogModel->getProductById($id); if (null !== $product) { $this->_cartModel->addItem($product, $value); } } $this->_cartModel->setShippingCost( $this->_getParam('shipping') ); return $this->_helper->redirector('view'); } } The Cart Controller has three actions that provide a way to: add: add cart items view: view the cart contents update: update cart items The addAction() first tries to find the product to be added to the cart. This is done by searching for the product by its productId field, which is passed either in the URL or by post using the Catalog Model. If the product is not found, then we throw an SF_Exception stating so. Next, we add the product to the cart using the addItem() method. When adding the product, we also pass in the qty. The qty can again be either in the URL or post. Once the product has been successfully added to the cart, we then need to redirect back to the page where the product was added. As we can have multiple locations, we send a returnto variable with the add request. This will contain the URL to redirect back to, once the item has been added to the cart. To stop people from being able to redirect away from the storefront, we prepend the baseurl to the redirect string. To perform the actual redirect, we use the redirector Action Helper's gotoUrl() method. This will create an HTTP redirect for us. The viewAction() simply assigns the Cart Model to the cartModel View property. Most of the cart viewing functionality has been pushed to the Cart View Helper and Forms, which we will create shortly. The updateAction() is used to update the Cart Items already stored in the cart. The first part of this updates the quantities. The quantities will be posted to the Action as an array in the quantity parameter. The array will contain the productId as the array key, and the quantity as the value. Therefore, we iterate over the array fi nding the product by its ID and adding it to the cart. The addItem() method will then update the quantity for us if the item exists and remove any with a zero quantity. Once we have updated the cart quantities, we set the shipping and redirect back to the viewAction. >> Continue Reading Creating a Shopping Cart using Zend Framework: Part 2
Read more
  • 0
  • 0
  • 5671

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

Real Content in PHP5 CMS: Part 2

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