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-managing-articles-using-k2-content-construction-kit
Packt
27 Oct 2010
8 min read
Save for later

Managing Articles Using the K2 Content Construction Kit

Packt
27 Oct 2010
8 min read
  Joomla! 1.5 Cookbook The reader would benefit from the previous article on Installation and Introduction of K2 Working with items AKA articles The power of K2 is in the idea of categorizing your data, thus making it easier to manage. This will be especially helpful as your site grows in content. Many sites are fully article-based and it is not uncommon to see a site with thousands of articles on it. In this section, we'll tackle some more category-specific recipes. You may have noticed by now that data does not show up as typical articles do in Joomla!. In other words, if you added an item, set it published and featured, it may not be displayed on your site because you have not set up a menu item to your K2 content. K2 will need to be added to your menu structure to display the items (articles) in K2. The first recipe will take into account a site that has been in operation for a while and has K2 added to it. Getting ready This section assumes you have installed K2 and have content on your site. How to do it... Make sure you have a full backup of the database and the files. Log in as the administrator. Open the K2 Dashboard. If you DID NOT import your content in, (see the first recipe), do so now. If you have ALREADY imported your content using the Import Joomla! Content button - DO NOT import again. You run the risk of duplicating all your content. Should this happen, you can go in and delete the duplicate items. This can be a time-consuming process. Open Article Manager | Content | Article Manager. Select all your articles from the Article Manager and unpublish. Open Menu Manager and find your Home menu.Now that we have unpublished content, we'll need to replace the traditional Joomla! content items with K2 content. Opening the Menu Manager and selecting the Home menu item will show this: As you can see under K2 there are several choices to display content on your site. I will choose Item | Item as my display mode. This will show my visitors content in article form. You can pick what works best for you. Now returning to the instructions: After choosing Menu Item Type - click Save. Open K2 Dashboard. Select Items.Here is a partial screenshot of the items in our sample site. As you can see, it now starts to take on a bit more traditional Joomla! look. I can choose featured articles, publish them, or note. Set the order they show up in, the category they belong to and more. When you import content, from Joomla!, the articles retain their identity from Section and Category configuration. For example, the Joomla! Community Portal listed in the preceding screenshot as belonging to the category Latest has a parent category of News. When you imported the content, sections became the new K2 top-level categories. All existing categories become subcategories of the new top level categories. As we added K2 to a working site with sections and category data already in place, I want to make sure they inherit from our master category. In our sample site, we see the following screen when we open the K2 categories from the K2 Dashboard: We instruct the new top-level categories to follow the master category as the model for the rest. The following instructions will show you how. Open K2 Dashboard. Click Categories. Open your imported top-level categories - for this site it's About Joomla! and News. Each of these has sub-categories. Click About Joomla! (or your equivalent). Change the Inherit parameter options from category to MASTER CATEGORY USE AS INHERIT ONLY. Make sure the Parent category stays set to –None--. Click Save.When done, it will look like this: Extra fields Did you notice the Associated "Extra Fields Group" is set to - None - ? You can change this parent category group to use an extra fields group and still keep the master category parameters. Each of the subcategories will inherit from the master category. By doing this, you can still control all the categories parameters simply by changing the master category. How it works... The category system as described here for K2 is a giant access-control system allowing you the flexibility to structure your site and data as you need. It also offers a means to control the 'look and feel' of the articles from a central place. When you import a Joomla! site into K2 you bring all the sections, content, articles, and other associated parts into it. Sections become new parent categories and the old categories become subcategories. This can be a bit confusing at first. One suggestion is to write out on paper what you want the site to look like, and then lay out your categories. You might find that the structure you had can be more user-friendly using K2 and you will want to change. This category system offers you nearly unlimited means to nest articles. In essence, a category can have unlimited categories under it. There is a limit to this in terms of management, but you get the idea. There's more... Using tags in K2 will give you the ability to improve your Search Engine Optimization or SEO on your site. Additionally, the use of tags will allow you to give your users the ability to follow the tags to other articles. In this section we'll review how to use Tags in K2. Tags are keywords or terms that are assigned to your content. This enables your visitors to quickly locate what they need by one word descriptions. Using Tags in K2 Tags can be created before an article is written or on the fly. I prefer on the fly as it will match the article. You can think of a tag almost as a dynamic index. Every time a tag is added to an article, it will show up in the K2 Tag Cloud module if you are using it. The more a single tag, such as Joomla!, is used in the content, the larger it appears in the K2 Cloud module. K2 Tag Clouds can benefit your search engine optimization and a navigational element. Here is an example of our K2 Tag Cloud: This is an image of our K2 Tag Cloud module. The more often a tag is added to an article, the larger it appears. Setting up your site for Tag Clouds K2 installs the K2 Tools module by default. The module has many functions, but for our purposes here, we'll use the Tag module. Log in to the Administrator Console of Joomla!. Click Extensions | Module Manager. Click New to create a new module. Find this for your new item: Once in there, give it a name and select its module location. On the right under Parameters, pull down the Select module functionality drop-down list as follows: Select Tag Cloud as shown in the preceding screenshot. Leave all the root categories set for none - this will enable K2 to pull in all the categories. Click Save. This particular module, has many functions and you can set up a new module to use any of the great tools built into it. Next you will want to add some tags to articles. As I said at the beginning of this article, you have two different ways to do this. You may add them to the article or you may add them to the Tag Manager. Let's quickly review the latter method. Open K2 Dashboard. Click Tags. You may see a list of tags there. If you wish to delete them, simply check the ones you want to remove and click Delete in the upper right-hand corner. Otherwise just leave them. Click New which will open the Details box. Fill in the tag; make sure it's published and click Save. This is an example of a filled out tag box (before save). Adding Tags on the fly This model allows you to tag the content as soon as you create it. If there are tags available, already such as those from the previous step, then you can add them. Open K2 Dashboard. Click Items. Select an item or click New to create an item. The field Tags will be blank, you can start to type in a field, such as K2 Content Creation Kit (as shown in the preceding screenshot). If it exists, then it will be available to be able to click and add. If there are no tags available, then simply type one in and click Return or add a comma. Here is an example item with tags. Here we have four tags, Security x, PHP x, Joomla x, K2 Content Creation Kit x. Any item (article) that has these tags will be easily found by both users and search bots. Let's see how our Tag Cloud looks now: You probably notice the changes, especially the addition of the new tag K2 Content Creation Kit. Clicking on that tag will yield two articles, and clicking on the Security tag yields three. Search engines can follow these links to better categorize your site. Users can get a sense of what is more important in terms of content from your site and it helps them navigate. Closing on this, I strongly suggest you spend time picking tags that are important on your site and is relevant to the purpose of it.
Read more
  • 0
  • 0
  • 3516

article-image-securing-bpel-process
Packt
27 Oct 2010
9 min read
Save for later

Securing a BPEL process

Packt
27 Oct 2010
9 min read
WS-BPEL 2.0 for SOA Composite Applications with IBM WebSphere 7 Define, model, implement and monitor real-world BPEL 2.0 business processes with SOA-powered BPM Develop BPEL and SOA composite solutions with IBM's WebSphere SOA platform Automate business processes with WS-BPEL 2.0 and develop SOA composite applications efficiently A detailed explanation of advanced topics, such as security, transactions, human workflow, dynamic processes, fault handling, and more—enabling you to work smarter Core concepts BPEL as a specification does not provide any security concepts that we could leverage. All security aspects are left to the BPEL engine or, in other words, to the BPEL engine wrapper. In WebSphere, BPEL processes are implemented as SCA components. So for BPEL processes, we can leverage all security constructs that SCA architecture offers. A BPEL process can be secured on an SCA component level so that only authorized users can access it through the usage of a security permission qualifier. This qualifier defines that role-specific users must be assigned for accessing a specific SCA component, in our case a BPEL process. Before such a BPEL process is deployed on the server, it is possible to map specific users to the role qualifiers. At runtime, it is possible to dynamically add or remove users to and from this role. A very common standardized way to expose BPEL process to the outside world is through the use of web services. In WebSphere, every SCA component can be exposed as a web service through a web service export. There are four web service export types supported, depending on the communication protocol (HTTP(S) or JMS), message exchange format (SOAP v1.1 or v1.2), and Java implementation framework (JAX-WS or JAX-RPC). Web services use SOAP as a message exchange format. SOAP relies on XML. The root element of a SOAP message is a <soap:Envelope> that contains <soap:Header> and <soap:Body>. In the body, there are usually input data for the service operation (payload). Other data (such as credentials, correlation, and others) are contained in the header. Web services in Java can be implemented with JAX-WS (Java API for XML Web Services) or JAX-RPC (a predecessor of JAX-WS called Java API for XML-based Remote Procedure Call) API. JAX-WS uses annotations introduced in Java SE 5 to make development and deployment of web services and their clients an easier task. JAX-WS is the preferred approach to service development. In WebSphere, JAX-WS supports WS-Policy sets to configure web service behavior in a declarative way. WS-Policy is a framework for expressing characteristics (like capabilities or requirements) of web services. It uses flexible and extensible constructs. Each policy is a collection of many policy alternatives, each containing policy assertions. Policy assertions are used to express web service characteristics. In WebSphere, specific WS-Policy policies are grouped together in policy sets, each policy set containing one or more WS-Policy policies. The main purpose of using WS-Policy in WebSphere is to specify different web service behaviors, for example, to specify different security aspects. One of the most important security specifications for web services supported in WebSphere is WS-Security. WS-Security (WSS) is a specification for delivering end-to-end security for web services. It provides an extension to SOAP (to 1.1 and 1.2 versions) for assuring message content integrity and confidentiality. WS-Security supports a variety of security models such as PKI, Kerberos, and SSL. Moreover, WS-Security supports multiple security token formats (username token, binary security token, XML token, and EncryptedData token), trusted domains, signature formats and algorithms (Exclusive XML Canonicalization, SOAP Message Normalization), and encryption technologies (symmetric and asymmetric). A WSS username token contains a username and is extensible to include possible authentication data, such as a password and additional data to increase security like a nonce (a unique identifier to prevent replay attacks) or timestamp (to prevent replay attacks by leveraging message expiration methods). A WSS binary security token provides only one XML element which contains binary data (for example, X.509 certificate or Kerberos ticket). An XML token is an abstract token that is concretized into WSS subsequent specifications. One of the tokens is a SAML token (Security Assertion Markup Language), which is used for exchanging authentication and authorization data between different security domains (identity providers and service providers). An EncryptedData token is an encrypted version of any token contained in a WSS header to provide token confidentiality. Securing a BPEL process We will explain how to secure a BPEL process with an example. We will expose the BPEL process as a web service. This way, a client will be able to call it in a standardized way. Next, we will create a new WS-Policy set that will require the client to provide a WS-Security header with UsernameToken containing the username and password for user authentication. We will attach this WS-Policy set to our BPEL process's web service export to protect the process. Only authenticated users will have access to the BPEL process. We will then test the example, with and without providing credentials, to see if security is working. Next, we will see that authentication information (user's identity) is not automatically propagated to the BPEL process. So, the process does not know which user called it. We will extract a user's identity from UsernameToken and propagate this identity to the BPEL process. Through another round of testing, we will see that the user who called the process will become the process instance owner. The final step in this example will be to configure the BPEL process in a way that only authenticated users, who are authorized, will be able to call it. To achieve this, we will have to define the security permission qualifier on our BPEL process, define a role that will have access to our BPEL process, and map users to this role to actually allow specific users to access our BPEL process. Later on, we will be able to add or remove users from the role to enable runtime access permission update for specific users. Exposing a BPEL process as web service We will expose a BPEL process as a web service with the help of the following steps: Let us take the Travel Approval BPEL process and open it in the WebSphere Integration Developer. The Travel Approval process is an SCA component with an interface. The Travel Approval process and surrounding components are shown in the assembly diagram represented in the following screenshot: The Travel Approval process sends an e-mail to confirm the reservation. You can download the sample from http://www.packtpub.com. The sample can be deployed to the IBM WebSphere Process Server using the IBM WebSphere Integration Developer. We can expose this process as a web service by generating a web service export. We can generate the web service export by right-clicking on the TravelApproval process and selecting Generate Export... and then Web Service Binding from the context menu, as shown in the following screenshot: The Travel Approval process implements more than one interface, so a dialog asks us which interface to expose. We should select the interface that is used to run the process, that is, the TravelApprovalPT interface, as shown in the following screenshot: Next, we have to select the transport protocol. There are four options as we can see in the following image. The first two are using the HTTP protocol with JAX-WS as the implementation framework and the SOAP protocol of versions 1.1 and 1.2 respectively (the name Simple Object Access Protocol behind the SOAP abbreviation was dropped in version 1.2). SOAP 1.2 brings several advancements over SOAP 1.1. For example, it assures better interoperability, better support standards like XML Information Set (a set of definitions for use in other specifications), is truly protocol independent, and has better and more formalized extensibility. The third option uses JAX-RPC as the implementation framework for SOAP 1.1. The last option doesn't use HTTP as the transport protocol but instead uses JMS (Java Message Service).We will use WS-Policy to define authentication rules, so we need the WSPolicy support that is offered only with JAX-WS binding. We will choose usage of SOAP messages of version 1.2, that is, SOAP1.2/HTTP, as the latest standard, as shown in the following screenshot. Alternatively, we could also use SOAP 1.1 with JAX-WS. At this point, we need to save our assembly diagram, because we will rename the newly generated export to have a more descriptive name. We will use the refactoring option for renaming SCA components, imports, and exports. Refactoring is enabled by right-clicking on our newly created web service export, TravelApprovalPTExport1 | Refactor... | Rename, as shown in the following screenshot: We choose TravelApprovalWS as the new name for the export and click on Refactor: When we created the web service export, WebSphere Integration Developer generated the following artifacts: Web service binding TravelApprovalWS_TravelApprovalPTHttpBinding in namespace http://packtpub.com/bpel/travel/Binding A service called TravelApprovalWS_TravelApprovalPTHttpService with port TravelApprovalWS_TravelApprovalPTHttpPort and endpoint address http://localhost:9080/SecuringBPELWeb/sca/TravelApprovalWS Note that the endpoint address can change when the web service is deployed on another server. We can see all these details if we select our web service export in the assembly diagram and bring up its properties by clicking on Properties | Binding: All these details are actually defined in the WSDL file that was generated in our SecuringBPEL_lib library, which contains data types and interfaces. We can find the TravelApprovalWS_TravelApprovalPTHttpPort WSDL file under Web Service Ports in SecuringBPEL_lib, as shown in the following screenshot: When we open this WSDL file, we can see all the content we described earlier (binding, service, port, address). Notice that the <wsdl:import> element imports the actual (abstract) interface from TravelApprovalPT.wsdl: (Move the mouse over the image to enlarge.) In this section, we have created a web service export for our BPEL process and examined the details that occurred behind the scenes.
Read more
  • 0
  • 0
  • 2608

article-image-jquery-user-interface-plugins-tooltip-plugins
Packt
27 Oct 2010
6 min read
Save for later

jQuery User Interface Plugins: Tooltip Plugins

Packt
27 Oct 2010
6 min read
  jQuery Plugin Development Beginner's Guide Build powerful, interactive plugins to implement jQuery in the best way possible Utilize jQuery's plugin framework to create a wide range of useful jQuery plugins from scratch Understand development patterns and best practices and move up the ladder to master plugin development Discover the ins and outs of some of the most popular jQuery plugins in action A Beginner's Guide packed with examples and step-by-step instructions to quickly get your hands dirty in developing high quality jQuery plugins         Read more about this book       (For more resources on jQuery, see here.) Before we get started, there is another little thing worth mentioning: provide many different opportunities to introduce new concepts and ideas, even while keeping the complexity of the whole plugin at a minimum. We can now go on to create our plugin, starting with basic functionalities, and subsequently adjusting its goals. We will add new, improved functionalities that, however, do not make the whole code look too difficult to understand—even after some time or for someone who's just starting out with jQuery. Tooltip plugins in general A lot has been said about tooltip plugins, but it's worth repeating the most important points with particular regard to the way tooltips are supposed to work, and how we want our tooltip to behave. First of all, we might want to get an idea of what tooltips look like and a sample of what we will accomplish by the end of this article. Here is an example: Also, with some more work and proper application of effects, images, and other relatively advanced techniques, we can also obtain something more complex and nicer looking, thus giving the user the chance to specify the style and behavior for the tooltip, as follows: The idea is actually very simple. The elements we have selected will trigger an event every time we hover the mouse pointer over them. The tooltip will then pop out, right at the mouse cursor position, retrieving the text portion from the title attribute of the said element. Finally, whenever we move the mouse over the same element, the plugin will move and follow the mouse cursor until it goes off the boundaries of the element. Positioning the tooltip The first problem we have to face is, of course, how to make the tooltip appear in the right position. It would be no trouble at all if we just had to make some text, image, or anything else show up. We've done it many times and it's no problem at all—just make their positioning absolute and set the right top and side distances. However, we need to take into account the fact that we don't know exactly where the mouse cursor might be and, as such, we need to calculate distances based upon the mouse cursor position itself. So, how can we do it? It's simple enough; we can use some of the JavaScript event properties to obtain the position. Unfortunately, Internet Explorer always tries to put a spoke in our wheel. In fact, the magnificent browser does not (according to this table, which is quite accurate: http://www.quirksmode.org/dom/w3c_cssom.html#mousepos) support pageX and pageY, which would normally return the mouse coordinates relative to the document. So we need to think about a workaround for Internet Explorer, as jQuery (from version 1.0.4 onwards) does not normalize some of the event properties according to W3C standards (http://api.jquery.com/category/events/event-object/). The following diagram (also provided in the " target="_blank">code bundle) should clarify what the visible viewport is (that is, the browser window—the red box): Whenever we scroll down, different parts of the document (blue) are shown through the browser window and hidden due to space constraints. The scroll height (green) is the part of the document currently not displayed. Custom jQuery selectors Suppose we have a page with some text written in, which also contains a few links to both internal pages (that is, pages on the same server) and external websites. We are presented with different choices in terms of which elements to apply the tooltip to (referring to links as an example, but they apply to any kind of element as well), as follows: All the links All the links with a specific class (for example, tooltip) All the links with the title attribute not empty All the links pointing to internal pages All the links pointing to external websites Combinations of the above We can easily combine the first three conditions with the others (and with themselves) using CSS selectors appropriately. For example: $("a"), all the links $("a.tooltip"), links having a tooltip class $("a[title]"), links with a title attribute (still have to check if empty) $("a.tooltip[title]"), links with a tooltip class and a title attribute As for internal and external pages, we have to work with jQuery selectors instead. Time for action – creating custom jQuery selectors Although jQuery makes it easy to select elements using standard CSS selectors, as well as some other selectors, jQuery's own selectors are the ones that help the developer to write and read code. Examples of custom selectors are :odd, :animated, and so on. jQuery also lets you create your own selectors! The syntax is as follows: // definition$.expr[':'].customselector = function(object, index,properties, list) { // code goes here};// call$("a:customselector") The parameters are all optional except for the first one (of course!), which is required to perform some basic stuff on the selected object: object: Reference to current HTML DOM element (not jQuery, beware!) index: Zero-based loop index within array properties: Array of metadata about the selector (the 4th argument contains the string passed to the jQuery selector) list: Array of DOM elements to loop through The return value can be either: true: Include current element false: Exclude current element Our selector (for external links detection) will then look, very simply, like the following code: $.expr[':'].external = function(object) { if(object.hostname) // is defined return(object.hostname != location.hostname); else return false;}; Also note that, to access the jQuery object, we have to use the following (since object refers to the DOM element only!): $.expr[':'].sample = function(object) { alert('$(obj).attr(): ' + $(object).attr("href") + 'obj.href: ' + object.href);}; Merging pieces together We have slowly created different parts of the plugin, which we need to merge in order to create a working piece of code that actually makes tooltips visible. So far we have understood how positioning works and how we can easily place an element in a determined position. Also, we have found out we can create our own jQuery selectors, and have developed a simple yet useful custom selector with which we are able to select links pointing to either internal or external pages. It needs to be placed at the top of the code, inside the closure, as we will make use of the dollar symbol ($) and it may conflict with other software.
Read more
  • 0
  • 0
  • 2255

article-image-postgresql-9-balancing-hardware-spending
Packt
27 Oct 2010
10 min read
Save for later

PostgreSQL 9: Balancing Hardware Spending

Packt
27 Oct 2010
10 min read
One of the reasons that working with open-source databases such as PostgreSQL can be so effective is that every dollar you save on software licensing can be put toward better hardware instead. The three main components you'll need to balance in your budget are CPUs, memory, and disks, with the disk controller as a related and critical part too. CPUs Currently, available processors are bundling at least two and possibly as many as eight cores into each CPU, making the core count the figure of merit for most database applications. There are two basic decisions you need to make while deciding which CPU solution would best match your database application: Which processor family? Nowadays, this normally boils down to choosing among the various 64 bit product lines from Intel or AMD, although there are some other less popular choices still floating around (Itanium, SPARC, and so on). Do you get more cores or faster cores? These choices are sometimes more tied together than you might think. Currently, Intel has a lead in delivering individual processor cores that are the fastest around, often due to faster transfers between the processor and system RAM. But the processors and related parts are more expensive too. AMD still is competitive at providing more cores per dollar, and their server class designs normally do a good job making the best of the memory available to each core. But if what you want is many more affordable cores instead, that's where AMD is stronger. AMD also has a better history of making its fastest processors available in configurations with many sockets, when you want to put more than two physical CPUs into a single server. The best way to figure out which class of database app you have—more cores or faster cores—is to monitor an existing server using tools such as top. If there's a small number of processes running using a single CPU each, that's the sort of workload where faster cores are better. That tends to happen if you have giant queries running in a batch fashion, for example when large quantities of data need to be sorted to deliver any single report. But if all the CPUs are active with many more concurrent processes instead, then you'd likely benefit better from more cores. That's normally what you'll see in applications with a larger user count, such as databases backing web applications. If you don't have the benefit of a working system to inspect, you can try to guess which type of situation you're in by noting the limitations of the database. PostgreSQL does not allow splitting a single query across more than one core, what's called parallel query by some other databases that support it. That means that if you have any one query or small number of queries that must run as fast as possible, the only way to do that is to prioritize getting faster cores. Another situation where getting a faster core is the better choice is if you need to prioritize data loading or export situations. PostgreSQL's best performing data import method, COPY, can easily become (but isn't always) limited by CPU performance, where that turns into the bottleneck for operations. While it's possible to split input files into pieces and load them in parallel, that's something you'll need to build or acquire yourself, rather than something the server knows how to do for you. Exporting a copy of the database using the pg_dump utility is another example of something that can become CPU limited on some systems. Memory How much to prioritize memory for your application really depends on the size of the working set of data needed to handle the most common operations. Generally, adding more RAM will provide a significant performance boost. There are a few situations where you'd be better served doing something else instead: If your data set is small enough to fit into a smaller amount of RAM, adding more won't help you much. You probably want faster processors instead. When running applications that scan tables much larger than what you can feasibly purchase as RAM, such as in many data warehouse situations, you might be better served by getting faster disks rather than more memory. The normal situation where more memory helps most is when the data you access frequently will fit with the larger amount, but not with the smaller. This happens more often than you might think because of the way database B-tree indexes are stored. Even if you can't fit the entirety of a table or even its index in memory, being able to store a good sized fraction of the index can mean that index-based data lookups will be significantly sped up. Having the most "popular" blocks from the top of the tree structure cached is helpful even if you can't fit all the leaves into memory too. Once you have an application running, you can usually get a much better idea how memory is being used by looking inside the PostgreSQL buffer cache (and potentially inside the operating system one as well) and seeing what data it prefers to keep around. Disks While it's always possible to run into situations where the CPU in your database server is its bottleneck, it's downright likely that you'll run into a disk bottleneck— particularly if you only have a drive or two in the system. A few years ago, the basic two choices in hard drives were the inexpensive ATA (also known as IDE) drives used in desktops versus the more serious SCSI drives aimed at servers. Both technologies have marched forward, and the current choice you're most likely to run into when configuring a database server is whether to use Serial ATA (SATA) or Serial Attached SCSI (SAS). It's possible to find nearly identical drives available in both interfaces, and there are even drive controllers that allow attaching either kind of drive. Combined with a narrowing performance difference between the two, picking between them is harder than ever. The broad parameters of each technology are straightforward to compare. Here's the state of things as this is being written: SAS disks: The maximum available RPM is higher: 10,000 or 15,000 Not as much drive capacity: 73 GB-1 TB are popular sizes Cost per MB is higher SATA disks: Drives typically have a slower RPM: 7200 is standard, some 10,000 designs exist such as the Western Digital VelociRaptor Higher drive capacity: 2 TB available Cost per MB is lower Generally, you'll find individual SAS disks to be faster even than SATA ones with similar specifications. In particular, you're likely to see better seek performance on random I/O due to faster drive mechanics in SAS, and sometimes a faster transfer rate from the disk too. Also, because the SAS drives have supported advanced features such as command queuing for longer, it's more likely your operating system will have matching support to take advantage of them. RAID The Redundant Array of Inexpensive Disks (RAID) approach is the standard way to handle both the performance and reliability limitations of individual disk drives. A RAID array puts many disks, typically of exactly the same configuration, into a set that acts like a single disk—but with either enhanced performance, reliability, or both. In some cases the extra reliability comes from computing what's called parity information for writes to the array. Parity is a form of checksum on the data, which allows reconstructing it even if some of the information is lost. RAID levels that use parity are efficient from a space perspective at writing data in a way that will survive drive failures, but the parity computation overhead can be significant for database applications. The most common basic forms of RAID arrays used are: RAID 0: It is also called as Striping. Multiple disks are used at the same time, spreading reads and writes over each of them in parallel. This can be almost a linear improvement (two disks reading twice as fast as a single one), but a failure on any volume in the set will lose all the data. RAID 1: It is also called as Mirroring. Here more copies of the same data are put onto multiple disks. This can sometimes improve performance—a good RAID 1 mirroring across two disks might handle two reads by sending one to each drive. Reads executed in parallel against both drives can effectively double average seeks per second. But generally, the reason for RAID 1 is redundancy: if a single drive fails, the system will continue operating using the other one. RAID 10 or 1+0: This first takes pairs of disks and mirrors then using RAID 1. Then, the resulting set is striped using RAID 0. The result provides both high performance and the ability to tolerate any single disk failure, without as many ways for speed to suffer in the average and worst case as RAID 5/6. RAID 10 is particularly appropriate for write-heavy environments, where the parity computation overhead of RAID 5/6 can cause disk performance to suffer. Accordingly, it's the preferred RAID level for high-performance database systems. RAID 5: It is also called as Striped with Parity. This approach sits midway between 0 and 1. You stripe data across multiple drives similarly to RAID 0, which improves read performance. But some redundant data is added to a parity drive. If one of the disks in the array is lost, the missing data can be recomputed from the ones left using that parity information. While this is efficient in terms of how little space is wasted relative to the tolerance for disk failures provided, write performance in particular can suffer in RAID 5. RAID 6: Similar to RAID 5, except with more parity information, enabling survival even with two disk failures. It has the same fundamental advantages and disadvantages. RAID 6 is an increasingly common way to cope with the fact that rebuilding a RAID 5 array after a disk loss can take a really long time on modern, high capacity drives. The array has no additional fault tolerance during that period, and seeing a second drive failure before that rebuild finishes is not that unlikely when it takes many hours of intense disk activity to rebuild. Disks manufactured in the same batch are surprisingly likely to fail in groups. To be fair in any disk performance comparison, you need to consider that most systems are going to have a net performance from several disks, such as in a RAID array. Since SATA disks are individually cheaper, you might be able to purchase considerably more of them for the same budget than had you picked SAS instead. If your believe your application will get faster if it is spread over more disks, being able to buy more of them per dollar spent can result in an overall faster system. Note that the upper limit here will often be your server's physical hardware. You only have so many storage bays or controllers ports available, and larger enclosures can cost more both up-front and over their lifetime. It's easy to find situations where smaller numbers of faster drives—which SAS provides—is the better way to go. This is why it's so important to constantly benchmark both hardware and your database application, to get a feel of how well it improves as the disk count increases.
Read more
  • 0
  • 0
  • 5604

article-image-installation-and-introduction-k2-content-construction-kit
Packt
27 Oct 2010
5 min read
Save for later

Installation and Introduction of K2 Content Construction Kit

Packt
27 Oct 2010
5 min read
  Joomla! 1.5 Cookbook Over 60 quick and direct recipes to help you overcome common Joomla! queries. Find quick solutions to common Joomla! problems Part of Packt's Cookbook series: Each recipe is a carefully organized sequence of instructions to complete the task as efficiently as possible Look at recipes that cover the portions of Joomla! 1.6 that are brand new Over 60 practical recipes covering a range of site management and core Joomla! activities Generally speaking, a basic article is a simple matter to create and manage. When a site gets large with a lot of articles, it can be unwieldy. This need is filled by a Content Construction Kit or CCK. Several good CCK's exist in the Joomlasphere and each has its own unique means to accomplish the task of content management and creation. For our purposes we'll look at the CCK known as K2. K2 provides you an integrated solution that features rich content forms - extending the basic article. This is an article with additional fields that can contain the article images, videos, image galleries or even attachments. Add nested-level categories and tags to that, and you have a very powerful system. In this article we'll look at the installation and use of K2. Installation and introduction of K2 In this recipe you will go through the steps to install and configure K2 in your Joomla! site. Getting ready You will want to download K2 from the following URL: http://getk2.org Additionally, you may want to have a development site to install and learn this on rather than your production site. How to do it... Installation of K2 works like any other Joomla! extension. Be sure and backup your files and database before beginning - this way you can easily roll back should something go wrong. Download K2. Log in to your Joomla! site as the Super Administrator. Click Extensions | Install / Uninstall. Browse, locate, and upload the K2 package. Install.Installation of K2 should be complete. If you are not running the Joom!Fish language translation extension on your site, you will see an informational message stating that K2 did not install the content elements. Joom!Fish is used to translate your site into other languages. If you have no plans on using this, then ignore the message. Now when you go to Components, you'll see the K2 icon. Clicking it will show you this screen: The next step is to bring all of our current content (articles, media, and so on) into K2. While in the K2 Dashboard, look at the upper right-hand side for the Import Joomla! content button. You will see this message (or a similar one depending on your K2 version): Click Ok to start the process. Once complete, you'll see all your content now shows up in the K2 Items menu. Here are the sample Joomla! data items that have been imported. Keep in mind that all of your pre-existing Joomla! sections, categories, and articles are still in the core Joomla! areas. All these items are simply duplicated in K2 now. If you have existing content take a few minutes and go into the Article Manager of Joomla! and unpublish them. You may need to update menu links to your new K2 categories and articles as necessary. Additionally, you'll note that the main Joomla! administrator page has been updated to include the K2 dashboard. Further configuration of K2 K2 has a powerful, nested-category system. It offers you the ability to configure in detail, each category, allowing any category to inherit from another category's parameter settings. Let's use this feature of K2 to configure a master category that will allow other categories to inherit the settings. This means you can change one category and affect them all at the same time – which is quite time-saving. A word of caution, this could impact your search engine-friendly URLs depending on the extension you use to create them. Open the K2 Dashboard. Select Categories. Click New in the upper right-hand side. Fill in the following details: Title is the title of your category Title Alias is the internal reference Parent category should be left as –None-- - all other categories will inherit from this category Inherit parameter options from category should be left as –None-- Associated "Extra Fields" Group should be left as None. Published is Yes Access Level is Public I have filled mine out and the following screenshot is the completed Details section. For now, this is all you'll need to do. Later recipes will utilize this: This is my master category that all other categories will seek out their parameters from. The reason for this will be clear shortly. When you have finished, save the category. How it works... K2 is nearly a content management system, like Joomla!, in itself. It actually substitutes many of the native Joomla! functions like article management, section, and category as well as providing some access control functions. When using K2, you will no longer use many of the native Joomla! menus, but rather you will use the K2 version of the tools. If you install this component, it has the ability to pull in all the data (users, articles) into K2 through the import feature. There's more... In the previous recipe, we set up a master category; now we'll configure it here.
Read more
  • 0
  • 0
  • 2909

article-image-postgresql-9-reliable-controller-and-disk-setup
Packt
27 Oct 2010
10 min read
Save for later

PostgreSQL 9: Reliable Controller and Disk Setup

Packt
27 Oct 2010
10 min read
Accelerate your PostgreSQL system Learn the right techniques to obtain optimal PostgreSQL database performance, from initial design to routine maintenance Discover the techniques used to scale successful database installations Avoid the common pitfalls that can slow your system down Filled with advice about what you should be doing; how to build experimental databases to explore performance topics, and then move what you've learned into a production database environment Covers versions 8.1 through 9.0 PostgreSQL uses a Write-Ahead Log (WAL) to write data in a way that survives a database or hardware crash. This is similar to the log buffer or REDO log found in other databases. The database documentation covers the motivation and implementation of the WAL at http://www.postgresql.org/docs/current/ static/wal.html To quote from that introduction: WAL's central concept is that changes to data files (where tables and indexes reside) must be written only after those changes have been logged, that is, after log records describing the changes have been flushed to permanent storage. This procedure ensures that if your application has received a COMMIT for a transaction, that transaction is on permanent storage, and will not be lost even if there is a crash. This satisfies the durability portion of the ACID (atomicity, consistency, isolation, durability) expectations databases aim to satisfy. Write-back caches The CPUs and memory in your server are quite fast compared to its disk drives. Accordingly, making the rest of the system wait for the disks, particularly when things need to be written out, can drag overall performance down heavily. Systems that wait for the disks to complete their writes before moving into their next task are referred to as having a write-through cache. While the data may be stored temporarily in a memory cache, until it's made it all the way through to the physical disk, any write an application requested isn't considered complete. The normal solution to making that faster is to introduce a different type of write cache between the program doing the writing and disks. A write-back cache is one where data is copied into memory, and then control returns to the application that requested the write. Those writes are then handled asynchronously, at some future time dictated by the design of the write-back cache. It can take minutes before the data actually makes it to disk. When PostgreSQL writes information to the WAL, and sometimes when it writes to the regular database files too, that information must be "flushed to permanent storage" in order for the database's crash corruption defense mechanism to work. So what happens if you have a write-back cache that says the write is complete, but it really isn't? People call these lying drives, and the result can be very bad: If you have a system with a write-back cache, and a system crash causes the contents of that write-back cache to be lost, this can corrupt a PostgreSQL database stored on that drive and make it unusable. You can discover it takes expert intervention to even get the database to start again, and determining what data is damaged will be difficult. Consider the case where you have committed a transaction. Details of that new transaction might be spread across two data blocks on the drive. Now, imagine that one of those made it to disk before the system crashed, but the other didn't. You've now left the database in a corrupted state: one block refers to a transaction that doesn't exist where it's supposed to in the other block. Had at least all of the data blocks related to the WAL been written properly, the database WAL could correct this error after the crash. But the WAL protection only works if it can get honest information about whether information has been written to the disks properly or not, and the "lying" write-back caches do not report that. Sources of write-back caching Servers are filled with write caches you need to be aware of: Operating system write cache. This cache can easily be gigabytes in size. Typically you can flush data out of this cache by forcing a "sync" operation on the block that needs to be stored on disk. On POSIX systems (which includes all UNIX-like ones), this is done with the fsync or fdatasync calls. In some cases, it's possible to write directly in a sync mode, which is effectively a write followed by fsync. The postgresql.conf setting wal_sync_method controls which method is used, and it's possible to disable this altogether to optimize for speed instead of safety. Disk controller write cache. You'll find a write cache on most RAID controller cards, as well as inside external storage such as a SAN. Common sizes right now are 128 MB to 512 MB for cards, but gigabytes are common on a SAN. Typically controllers can be changed to operate in the completely writethrough mode, albeit slowly. But by default, you'll normally find them in write-back mode. Writes that can fit in the controller's cache are stored there, the operating system is told the write is completed, and the card writes the data out at some future time. To keep this write from being lost if power is interrupted, the card must be configured with a battery. That combination is referred to as a battery-backed write cache (BBC or BBWC). Disk drive write cache. All SATA and SAS disks have a write cache on them that on current hardware is 8 MB to 32 MB in size. This cache is always volatile: if power is lost, any data stored in there will be lost and they're always write-back caches if enabled. How can you make sure you're safe given all these write-back caches that might lose your data? There are a few basic precautions to take: Make sure whatever file system you're using properly implements fsync calls, or whatever similar mechanism is used, fully. Monitor your driver controller battery. Some controller cards will monitor their battery health, and automatically switch from write-back to writethough mode when there is no battery or it's not working properly. That's a helpful safety measure, but performance is going to drop hard when this happens. Disable any drive write caches. Most hardware RAID controllers will do this for you, preferring their own battery-backed caches instead. Disk controller monitoring When you have a RAID controller card with a battery-backed cache, you probably expect you'll need to monitor the card to determine when disks fail. But monitoring controller battery health is an equally critical aspect of maintaining a reliable database system when you're using this technology. If the battery fails and you're using it in write-back mode, your writes are not safe. Similarly, if your power fails, you should prefer shutting the database server down after a few minutes of power loss to trying to keep it going. Integrating in power monitoring via a UPS or similar mechanism should be part of your database server configuration, so that a short outage results in an orderly shutdown. Consider the purpose of the controller battery to protect yourself from really unexpected outages, like someone tripping over the power cord. Even if the manufacturer claims the controller battery will last through days of downtime, that's not a good reason to believe you'll actually be safe for an extended outage. You should consider the battery as something you'd prefer to only use for some number of minutes of protection. That may be the reality, particularly in a case where the battery is older and has lost much of its capacity, and some controller batteries don't start out with very much capacity. Be sure to run your own tests rather than blindly believing the manufacturer specifications: your data depends on it. Better RAID controllers will automatically disable write-back mode if their battery stops working normally. If performance suddenly drops on an older server, this is one potential cause. Also don't forget that every UPS has a battery that degrades over time as well. That's all the more reason to arrange an orderly shutdown of your server during a power outage, rather than optimistically presuming you can keep it running until power returns. Disabling drive write caches If your card doesn't disable all the drive write caches, or if you're using a software RAID approach, you'll need to turn the caches off yourself. The best way to do this is to see if it's possible to change the default write cache state using the utilities provided by the drive manufacturer. You should be able to do this through software as well. Here is a sample session from a Linux system checking the write cache, toggling it off, confirming that change took, and then toggling it on again: # hdparm -I /dev/sda | grep "Write cache" * Write cache # sudo hdparm -W 0 /dev/sda /dev/sda: setting drive write-caching to 0 (off) write-caching = 0 (off) # hdparm -I /dev/sda | grep "Write cache" Write cache # hdparm -W 1 /dev/sda /dev/sda: setting drive write-caching to 1 (on) write-caching = 1 (on) Only the -W 0 configuration is completely safe for database use. The PostgreSQL WAL documentation suggests similar commands to use for other operating systems. Performance impact of write-through caching If you don't have a battery-backed write cache, and therefore can't utilize some memory-based cache to accelerate fsync writes, commit performance on your database can be quite bad. The worst-case here is where you have a single client that is issuing a commit after every statement it executes. The reality of how a hard drive works means that individual writes happen once each time the drive spins around. Here are the measurements for the common drive speeds available right now, with computed maximum commit rate:     Rotation speed Rotation time (ms) Max commits/second 5400 11.1 90 7200 8.3 120 10000 6.0 166 15000 4.0 250 It's important to realize how limiting this can be: If you have a common 7200 rpm hard drive, no single client can commit more than 120 transactions/second in any situation where all that's available is a write-back cache. It doesn't matter how many disks you have in a RAID array, or how you configure your software. You must have hardware with a battery, enabling a non-volatile write-back cache, in order to safely exceed this limit. Some PostgreSQL installs use a RAID controller card just for this purpose, to provide a BBWC, in Just a Bunch of Disks (JBOD) mode—where no RAID is being done on the controller at all. Sometimes disks are used directly, and others layer software RAID on top, which can have some advantages compared to hardware RAID. If you have more than one client, you'll get more done per commit. It's normal to see >500 committed transactions per second if you have a larger number of clients all committing regularly, because each flushed disk write will include any queued up commit requests from other clients, too. The other common technique here is to batch commits into larger pieces, perhaps going 1000 records at a time rather than a single one, in order to reduce the average impact of commit latency. Another approach for accelerating systems that don't have a usable write cache is asynchronous commit. Summary In this article we saw how accidentally using volatile write-back caching in disk controllers and drives can easily introduce database corruption. Further resources on this subject: UNIX Monitoring Tool for PostgreSQL [Article] Server Configuration Tuning in PostgreSQL [Article] PostgreSQL 9: Balancing Hardware Spending [Article]
Read more
  • 0
  • 0
  • 2665
Unlock access to the largest independent learning library in Tech for FREE!
Get unlimited access to 7500+ expert-authored eBooks and video courses covering every tech area you can think of.
Renews at $19.99/month. Cancel anytime
article-image-microsoft-dynamics-nav-2009-creating-wizard-style-form
Packt
26 Oct 2010
4 min read
Save for later

Microsoft Dynamics NAV 2009: Creating a Wizard-style Form

Packt
26 Oct 2010
4 min read
Microsoft Dynamics NAV 2009 Programming Cookbook Build better business applications with NAV Write NAV programs to do everything from finding data in a table to integration with an instant messenger client Develop your own .NET code to perform tasks that NAV cannot handle on its own Work with SQL Server to create better integration between NAV and other systems Learn to use the new features of the NAV 2009 Role Tailored Client Easy-to-read recipes with detailed explanations and images Maximize your learning with short tutorials that tell you exactly what you need to know without all of the fluff How to do it... Add a frame to the form. Set the following properties on the textbox: Add a label to the frame with the caption "Frame 1". Set the following properties on the Label: Copy the frame and paste two copies of it on the form. Change the labels in the new frames to be Frame 2 and Frame 3. Change the Name properties of the frames to Frame2 and Frame3 respectively. Your form should look like the one shown in the following screenshot: Add four buttons to the form beneath Frame 1. The name and caption properties on each should be Back, Next, Finish, and Cancel respectively. Add the following code to the OnOpenForm trigger: CurrForm.Frame1.XPOS := 0; CurrForm.Frame1.YPOS := 0; CurrForm.Frame2.XPOS := 0; CurrForm.Frame2.YPOS := 0; CurrForm.Frame3.XPOS := 0; CurrForm.Frame3.YPOS := 0; CurrForm.HEIGHT := CurrForm.Cancel.YPOS + CurrForm.Cancel.HEIGHT + 220; CurrForm.WIDTH := CurrForm.Cancel.XPOS + CurrForm.Cancel.WIDTH + 220; WizardStep := 1; ShowStep(TRUE); Add a function named ShowStep that takes in a boolean value named Show as a parameter. Add the following code to the function: CASE WizardStep OF 1: BEGIN CurrForm.Frame1.VISIBLE := Show; CurrForm.Frame2.VISIBLE := NOT Show; CurrForm.Frame3.VISIBLE := NOT Show; CurrForm.Back.ENABLED := NOT Show; CurrForm.Next.ENABLED := Show; CurrForm.Finish.ENABLED := NOT Show; END; 2: BEGIN CurrForm.Frame1.VISIBLE := NOT Show; CurrForm.Frame2.VISIBLE := Show; CurrForm.Frame3.VISIBLE := NOT Show; CurrForm.Back.ENABLED := Show; CurrForm.Next.ENABLED := Show; CurrForm.Finish.ENABLED := NOT Show; END; 3: BEGIN CurrForm.Frame1.VISIBLE := NOT Show; CurrForm.Frame2.VISIBLE := NOT Show; CurrForm.Frame3.VISIBLE := Show; CurrForm.Back.ENABLED := Show; CurrForm.Next.ENABLED := NOT Show; CurrForm.Finish.ENABLED := Show; END; END; Add the following code to the OnPush trigger of the Back button: ShowStep(FALSE); WizardStep -= 1; ShowStep(TRUE); Add the following code to the OnPush trigger of the Next button: ShowStep(FALSE); WizardStep -= 1; ShowStep(TRUE); Add the following code to the OnPush trigger of the Finish button: CurrForm.CLOSE; Add the following code to the OnPush trigger of the Cancel button: CurrForm.CLOSE Save and close the form. How it works... The form contains three frames, only one of which is visible at any given time. In the design view, you can see that our form is quite wide and tall, but that would not look right when displaying a wizard form. That's why we place code in the OnOpenForm trigger. The first set of lines places all of the frames on top of each other. The middle set changes the width and height of the form. Finally, the third sets the appropriate frames to be visible or not and enables the correct buttons. Our custom method ShowStep decides what should be visible and what should not. It is just a large CASE statement based on the WizardStep variable. On the first frame for example, we can't move backwards to disable the Back button. We can't finish until we get to the last frame so that the Finish button is disabled until that point. On the Back and Next buttons we decrement and increment the WizardStep variable so that the ShowStep method knows what to do. Other than the initial opening of the form we always call the function with FALSE as a parameter to "undo" what is currently displayed, change the WizardStep variable, and call the function with parameter TRUE to display new information. Summary In this part of the article series we covered: Creating a Wizard-style Form In the next part we will cover Updating Parent and Subform. Further resources on this subject: Microsoft Dynamics NAV 2009: Designing Forms Microsoft Dynamics NAV 2009: Creating a Matrix Form
Read more
  • 0
  • 0
  • 1687

article-image-php-5-social-networking-implementing-public-messages
Packt
26 Oct 2010
7 min read
Save for later

PHP 5 Social Networking: Implementing Public Messages

Packt
26 Oct 2010
7 min read
PHP 5 Social Networking Create a powerful and dynamic Social Networking website in PHP by building a flexible framework   Build a flexible Social Networking framework using PHP which can be extended to fit the needs of any Social Networking site Develop a suitable structure for our framework, with MVC to structure the architecture and a Registry to store core Objects Allow users to connect and communicate with each other using communication with friends list, flexible user profiles, messages, discussions, and much more Plan marketing and scaling strategies, to entice more users and ensure the site can cope with the demand Packed with real-world code and clear explanation, this book uses an ongoing case study for creating a Social Networking framework Throughout the course of this article we will be using a social networking site for keepers of pet dinosaurs (of course nobody owns a real pet dinosaur, but for the sake of this article, let's pretend!), which we will call DinoSpace. Public messages The status stream fully supports public messages and streaming them to the Dino Space members. What we don't yet have, however, is support for users to post messages on the profiles of other users, so, let's add that in now. Controller A user should only be able to post a message on another user's profile if they are connected. The post message form should only be displayed if the users are connected. Similarly, a public message post should only be processed if the two users are connected. The controller also needs to display messages that have been posted on a user's profile too. Displaying profile messages If we look at our Profilestatusescontroller (controllers/profile/ profilestatusescontroller.php), in the listRecentStatuses method, we have our query for listing recent profile statuses: $sql = "SELECT t.type_reference, t.type_name, s.*, p.name as poster_name FROM statuses s, status_types t, profile p WHERE t.ID=s.type AND p.user_id=s.poster AND p.user_id={$user} ORDER BY s.ID DESC LIMIT 20"; At the moment, this query pulls in any posts on a user's profile by the user whose profile it is. If that user has made a post on someone else's profile, the message instead shows on the user's own profile, which we don't want. We need to change this to pull in the profiles table twice, once for the user who made the post, and again for the user whose profile is being viewed. We will also want to only pull in posts made on the user's profile, and not posts made by the user on another user's profile (though this is something we can expand on in the future, perhaps to indicate that a user has made a post on the profile of another user). The following query should meet our requirements nicely: $sql = "SELECT t.type_reference, t.type_name, s.*, pa.name as poster_name FROM statuses s, status_types t, profile p, profile pa WHERE t.ID=s.type AND p.user_id=s.profile AND pa.user_id=s.poster AND p.user_id={$user} ORDER BY s.ID DESC LIMIT 20"; Now, if we view a user's profile, we see their own status updates, and messages posted on their profile by other users, as shown in the following screenshot: Displaying the post message box The listRecentStatuses method we were just editing is the method we need to edit to display the post message box. This box should only be displayed if the user is logged in, and is connected to the user. If the user is viewing their own profile, then they should see a box to update their own status: // post status / public message box if( $this->registry->getObject('authenticate')->isLoggedIn() == true ) { $loggedInUser = $this->registry->getObject('authenticate')- >getUser()->getUserID(); If the logged in user is viewing their own profile, then we add the update template to the view, so they can update their status: if( $loggedInUser == $user ) { $this->registry->getObject('template')->addTemplateBit('status_ update', 'profile/statuses/update.tpl.php' ); } else { If the user isn't viewing their own profile, but is logged in, we get any connections the user has: require_once( FRAMEWORK_PATH . 'models/relationships.php' ); $relationships = new Relationships( $this->registry ); $connections = $relationships->getNetwork( $user, false ); if( in_array( $loggedInUser, $connections ) ) { If the user is connected to the user whose profile they are viewing, then we allow them to post a message on the users profile with the post template: $this->registry->getObject('template')->addTemplateBit( 'status_update', 'profile/statuses/post.tpl.php' ); } else { If the user isn't connected to the user, or isn't logged in, then we simply remove the template tag from the view so they don't see any update or post box on the page: $this->registry->getObject('template')->getPage()- >addTag( 'status_update', '' ); } } } else { $this->registry->getObject('template')->getPage()- >addTag( 'status_update', '' ); } Now, we need to process status updates and profile posts, and create the templates that make up the final aspect of our view. Process a new message The same logic that we used to determine whether the user should see a post form is what we need to use to determine if we should process a status update, or public message submission. Status model To save the status update or public profile post in the database, we will need a status model; as with our previous models, this simply needs to represent the fields from the database, with setter methods for these fields, and a save method to insert a new record into the database. In the future, we may wish to extend this to pull in statuses from the database, and save changes to them, as well as deleting statuses, perhaps if the owner of the message or the owner of the profile the message was posted on wishes to edit or delete it. The following is suitable code for our status model (models/status.php): <?php /** * Status model */ class Status { /** * The registry object */ private $registry; /** * Statuses ID */ private $id; /** * Poster of the status update / profile message */ private $poster; /** * The profile the status update / profile message was posted on */ sprivate $profile; /** * Type of status */ private $type; /** * The update / profile message itself */ private $update; /** * Reference for the type of status */ private $typeReference = 'update'; /** * Constructor * @param Registry $registry the registry object * @param int $id ID of the status update / profile message * @return void */ public function __construct( Registry $registry, $id=0 ) { $this->registry = $registry; $this->id = 0; } /** * Set the poster of the status / profile message * @param int $poster the id of the poster * @return void */ public function setPoster( $poster ) { $this->poster = $poster; } /** * Set the profile that the message / status is posted on * @param int $profile the profile ID * @return void */ public function setProfile( $profile ) { $this->profile = $profile; } /** * Set the status / profile message itself * @param String $status * @return void */ public function setStatus( $status ) { $this->status = $status; } /** * Set the type of status / profile message * @param int $type * @return void */ public function setType( $type ) { $this->type = $type; } /** * Set the type reference, so we can get the type ID from the database * @param String $typeReference the reference of the type * @return void */ public function setTypeReference( $typeReference ) { $this->type = $typeReference; } /** * Generate the type of status based of the type reference * @return void */ public function generateType() { $sql = "SELECT * FROM status_types WHERE type_reference='{$this->typeReference}'"; $this->registry->getObject('db')->executeQuery( $sql ); $data = $this->registry->getObject('db')->getRows(); $this->type = $data['ID']; } /** * Save the status / profile message * @return void */ public function save() { if( $this->id == 0 ) { $insert = array(); $insert['update'] = $this->status; $insert['type'] = $this->type; $insert['poster'] = $this->poster; $insert['profile'] = $this->profile; $this->registry->getObject('db')- >insertRecords( 'statuses', $insert ); $this->id = $this->registry->getObject('db')->lastInsertID(); } } } ?> Now that we have some functionality to easily insert the status into the database, we need to update our profile controller to process the new status update.
Read more
  • 0
  • 0
  • 6649

article-image-microsoft-dynamics-nav-2009-creating-matrix-form
Packt
26 Oct 2010
5 min read
Save for later

Microsoft Dynamics NAV 2009: Creating a Matrix Form

Packt
26 Oct 2010
5 min read
  Microsoft Dynamics NAV 2009 Programming Cookbook Build better business applications with NAV Write NAV programs to do everything from finding data in a table to integration with an instant messenger client Develop your own .NET code to perform tasks that NAV cannot handle on its own Work with SQL Server to create better integration between NAV and other systems Learn to use the new features of the NAV 2009 Role Tailored Client Easy-to-read recipes with detailed explanations and images Maximize your learning with short tutorials that tell you exactly what you need to know without all of the fluff The reader would benefit by reading the previous article on Microsoft Dynamics NAV 2009: Designing Forms How to do it... Add a global function CalculateData that returns a text variable. Add a global function ColumnHeader that returns a text variable. Add a matrix box to the form. Set the following properties on the matrix box control: Set the following property on the form: Add the No. and Name fields to the left-hand side of the matrix box using the Field menu. Add a textbox to the right-hand side of the matrix box. Set the following property on the textbox: Add a textbox as a column header above that textbox. Set the following property on the textbox: Add the following code to the ColumnHeader function. EXIT(CurrForm.MatrixBox.MatrixRec."No."); Add the following local variables to the CalculateData function: Add the following code to the CalculateData function ItemLedgerEntry.RESET; ItemLedgerEntry.SETCURRENTKEY("Source Type", "Source No.", "Item No.", "Variant Code", "Posting Date"); ItemLedgerEntry.SETRANGE("Source Type", ItemLedgerEntry."Source Type"::Customer); ItemLedgerEntry.SETRANGE("Source No.", "No."); ItemLedgerEntry.SETRANGE("Item No.", CurrForm.MatrixBox.MatrixRec."No."); ItemLedgerEntry.SETRANGE("Entry Type", ItemLedgerEntry."Entry Type"::Sale); IF ItemLedgerEntry.FINDSET THEN REPEAT ItemLedgerEntry.CALCFIELDS("Sales Amount (Actual)"); TotalSales := TotalSales + ItemLedgerEntry."Sales Amount (Actual)"; UNTIL ItemLedgerEntry.NEXT = 0; EXIT(FORMAT(TotalSales)); After running the resulting form, you should see something similar to the following screenshot: How it works... A matrix form consists of two tables and some calculation based on those two tables. One set of records runs vertically along the left-hand side of the matrix box while the other set runs horizontally across the top. A grid is displayed on the rest of the form displaying a calculated value. We'll examine each of these pieces individually. We begin by creating a normal form that is bound to the Customer table. For this special form we add a matrix box control. The left-hand side operates exactly the same as a standard list form. It will display all of the customers and there will be a scrollbar to look through the list. As we don't want the user to change anything on this form, we set the Editable property of the matrix box to No. We will also have to write code that refers to this control so we must give it a name. Also, the matrix box itself operates on a table. In this case it is the Item table. As there is so much data stored in a table, we have to tell the control what we want to see. That's why we add a textbox as a column header to the top of the form. The source expression for that textbox is the ColumnHeader method. Let's take a look at the code there. EXIT(CurrForm.MatrixBox.MatrixRec."No."); CurrForm is the current form. MatrixBox is the value in the name property of our matrix box control. MatrixRec is the record in the matrix box that we are referring to (just like rec on a normal form). Finally, No. is the field from the MatrixSourceTable property(in this case the Item No). So our column headers will just be the Item Number from the Item table. Lastly, we have to tell the form how to calculate the data we want to see. We add another textbox to the form and give it a source expression of CalculateData, which is a function on our form. This function could return anything, but in our case it returns the amount a customer has spent on a specific item. Let's take a look at the important code that combines the data from both tables. ItemLedgerEntry.SETRANGE("Source No.", "No."); ItemLedgerEntry.SETRANGE("Item No.", CurrForm.MatrixBox.MatrixRec."No."); The Item Ledger Entry table already has fields that refer to the Customer table and to the Item table. The first filter uses the No. field from the source table (Customer). The second filter determines the current Item Number from the matrix box and uses it. Later in the function, a number is calculated and returned as a text variable. Summary In this part of the article series we covered: Creating a Matrix Form In the next part we will create a wizard-style form. Further resources on this subject: Microsoft Dynamics NAV 2009: Designing Forms Microsoft Dynamics NAV 2009: Creating a Wizard-style Form
Read more
  • 0
  • 0
  • 3911

article-image-php-5-social-networking-private-messages
Packt
26 Oct 2010
7 min read
Save for later

PHP 5 Social Networking: Private Messages

Packt
26 Oct 2010
7 min read
PHP 5 Social Networking We obviously need to keep private messages separate from the rest of the site, and ensure that they are only accessible to the sender and the receiver. While we could alter the public messages feature developed earlier, this would raise a few issues, such as being more difficult to tell whether the message being sent or read was private, and when using the Internet in a public area, the message would be shown on the area of the social network the user would most likely be visiting, which isn't ideal for private information. Because private messages will be separate from statuses, and won't need to make use of other media types to make them more interesting (though, we could set them up to make use of other media if we wanted), it makes sense for us to also use separate database tables and models for this feature. Database Our database needs provisions for the sender of the message, the recipient of the message, the subject of the message, and of course the message itself. We should also provide for if the message has been read, when the message was sent, and an ID for the message. The following illustrates a suitable structure for a messages table in our database:   Field Type Description ID Integer, Autoincrement, Primary Key Reference ID for the message Sender Integer The sender of the message Recipient Integer The recipient of the message Subject Varchar The subject the message relates to Sent Timestamp When the message was sent Message Longtext The contents of the message itself Read Boolean Indicates whether the message has been read or not More than one recipient? This database structure, and the code that follows, only supports one recipient per message. Our users might want to send to more than one recipient—feel free to add this functionality if you wish. Message model As with the majority of our database access, we require a model (models/message. php) to create, update, and retrieve message-related data from the database and encapsulate it within itself. It would also be helpful if the model pulled in a little more information from the database, including: A more user friendly representation of the date (we can get this via the MySQL DATE_FORMAT function) The name of the sender, by joining the messages table to the profile table The name of the recipient, by joining the messages table to the profile table again The first part of our model simply defines the class variables: <?php /** * Private message class */ class Message { /** * The registry object */ private $registry; /** * ID of the message */ private $id=0; /** * ID of the sender */ private $sender; /** * Name of the sender */ private $senderName; /** * ID of the recipient */ private $recipient; /** * Name of the recipient */ private $recipientName; /** * Subject of the message */ private $subject; /** * When the message was sent (TIMESTAMP) */ private $sent; /** * User readable, friendly format of the time the message was sent */ private $sentFriendlyTime; /** * Has the message been read */ private $read=0; /** * The message content itself */ private $message; The constructor takes the registry and ID of the message as parameters, if the ID has been defined, then it queries the database and sets the class variables. The database query here also formats a copy of the date into a friendlier format, and looks up the names of the sender and recipient of the message: /** * Message constructor * @param Registry $registry the registry object * @param int $id the ID of the message * @return void */ public function __construct( Registry $registry, $id=0 ) { $this->registry = $registry; $this->id = $id; if( $this->id > 0 ) { $sql = "SELECT m.*, DATE_FORMAT(m.sent, '%D %M %Y') as sent_friendly, psender.name as sender_name, precipient.name as recipient_name FROM messages m, profile psender, profile precipient WHERE precipient.user_id=m.recipient AND psender.user_id=m.sender AND m.ID=" . $this->id; $this->registry->getObject('db')->executeQuery( $sql ); if( $this->registry->getObject('db')->numRows() > 0 ) { $data = $this->registry->getObject('db')->getRows(); $this->sender = $data['sender']; $this->recipient = $data['recipient']; $this->sent = $data['sent']; $this->read = $data['read']; $this->subject = $data['subject']; $this->message = $data['message']; $this->sentFriendlyTime = $data['sent_friendly']; $this->senderName = $data['sender_name']; $this->recipientName = $data['recipient_name']; } else { $this->id = 0; } } } Next, we have setter methods for most of the class variables: /** * Set the sender of the message * @param int $sender * @return void */ public function setSender( $sender ) { $this->sender = $sender; } /** * Set the recipient of the message * @param int $recipient * @return void */ public function setRecipient( $recipient ) { $this->recipient = $recipient; } /** * Set the subject of the message * @param String $subject * @return void */ public function setSubject( $subject ) { $this->subject = $subject; } /** * Set if the message has been read * @param boolean $read * @return void */ public function setRead( $read ) { $this->read = $read; } /** * Set the message itself * @param String $message * @return void */ public function setMessage( $message ) { $this->message = $message; } The save method takes the class variables that directly relate to the messages table in the database and either inserts them as a new record, or updates the existing record: /** * Save the message into the database * @return void */ public function save() { if( $this->id > 0 ) { $update = array(); $update['sender'] = $this->sender; $update['recipient'] = $this->recipient; $update['read'] = $this->read; $update['subject'] = $this->subject; $update['message'] = $this->message; $this->registry->getObject('db')->updateRecords( 'messages', $update, 'ID=' . $this->id ); } else { $insert = array(); $insert['sender'] = $this->sender; $insert['recipient'] = $this->recipient; $insert['read'] = $this->read; $insert['subject'] = $this->subject; $insert['message'] = $this->message; $this->registry->getObject('db')->insertRecords( 'messages', $insert ); $this->id = $this->registry->getObject('db')->lastInsertID(); } } One getter method that we need, is to return the user ID of the recipient, so we can check that the currently logged in user has permission to read the message: /** * Get the recipient of the message * @return int */ public function getRecipient() { return $this->recipient; } We should also provide a method to delete the message from the database, should the user wish to delete a message: /** * Delete the current message * @return boolean */ public function delete() { $sql = "DELETE FROM messages WHERE ID=" . $this->id; $this->registry->getObject('db')->executeQuery( $sql ); if( $this->registry->getObject('db')->affectedRows() > 0 ) { $this->id =0; return true; } else { return false; } } Finally, we have a toTags method, which converts all of the non-object and non-array variables into template tags, so when we create a view message method in the controller, we simply need to construct the message object and call the toTags method: /** * Convert the message data to template tags * @param String $prefix prefix for the template tags * @return void */ public function toTags( $prefix='' ) { foreach( $this as $field => $data ) { if( ! is_object( $data ) && ! is_array( $data ) ) { $this->registry->getObject('template')->getPage()->addTag( $prefix.$field, $data ); } } } } ?> Messages model Similar to how we have a model for representing a single relationship and another for representing a number of relationships, we also need a model to represent a number of messages within the site. This is to handle the lookup of a user's private message inbox. <?php /** * Messages model */ class Messages { /** * Messages constructor * @param Registry $registry * @return void */ public function __construct( Registry $registry ) { $this->registry = $registry; } /** * Get a users inbox * @param int $user the user * @return int the cache of messages */ public function getInbox( $user ) { $sql = "SELECT IF(m.read=0,'unread','read') as read_style, m.subject, m.ID, m.sender, m.recipient, DATE_FORMAT(m.sent, '%D %M %Y') as sent_friendly, psender.name as sender_name FROM messages m, profile psender WHERE psender.user_id=m.sender AND m.recipient=" . $user . " ORDER BY m.ID DESC"; $cache = $this->registry->getObject('db')->cacheQuery( $sql ); return $cache; } } ?>
Read more
  • 0
  • 1
  • 4738
article-image-recovery-postgresql-9
Packt
25 Oct 2010
14 min read
Save for later

Recovery in PostgreSQL 9

Packt
25 Oct 2010
14 min read
Recovery of all databases Recovery of a complete database server, including all of its databases, is an important feature. This recipe covers how to do that in the simplest way possible. Getting ready Find a suitable server on which to perform the restore. Before you recover onto a live server, always take another backup. Whatever problem you thought you had could be just about to get worse. How to do it... LOGICAL (from custom dump -F c): Restore of all databases means simply restoring each individual database from each dump you took. Confirm you have the correct backup before you restore: pg_restore --schema-only -v dumpfile | head | grep Started Reload globals from script file as follows: psql -f myglobals.sql Reload all databases. Create the databases using parallel tasks to speed things along. This can be executed remotely without needing to transfer dumpfile between systems. Note that there is a separate dumpfile for each database. pg_restore -d postgres -j 4 dumpfile LOGICAL (from script dump created by pg_dump –F p): As above, though with this command to execute the script. This can be executed remotely without needing to transfer dumpfile between systems. Confirm you have the correct backup before you restore. If the following command returns nothing, then the file is not timestamped, and you'll have to identify it in a different way: head myscriptdump.sql | grep Started Reload globals from script file as follows: psql -f myglobals.sql Reload all scripts like the following: psql -f myscriptdump.sql LOGICAL (from script dump created by pg_dumpall): We need to follow the procedure, which is shown next. Confirm you have the correct backup before you restore. If the following command returns nothing, then the file is not timestamped, and you'll have to identify it in a different way: head myscriptdump.sql | grep Started Find a suitable server, or create a new virtual server. Reload script in full psql -f myscriptdump.sql PHYSICAL: Restore the backup file onto the target server. Extract the backup file into the new data directory. Confirm that you have the correct backup before you restore. $ cat backup_label START WAL LOCATION: 0/12000020 (file 000000010000000000000012) CHECKPOINT LOCATION: 0/12000058 START TIME: 2010-06-03 19:53:23 BST LABEL: standalone Check all file permissions and ownerships are correct and links are valid. That should already be the case if you are using the postgres userid everywhere, which is recommended. Start the server That procedure is so simple. That also helps us understand that we need both a base backup and the appropriate WAL files. If you used other techniques, then we need to step through the tasks to make sure we cover everything required as follows: Shutdown any server running in the data directory. Restore the backup so that any files in the data directory that have matching names are replaced with the version from the backup. (The manual says delete all files and then restore backup—that might be a lot slower than running an rsync between your backup and the destination without the –-update option). Remember that this step can be performed in parallel to speed things up, though it is up to you to script that. Check that all file permissions and ownerships are correct and links are valid. That should already be the case if you are using the postgres userid everywhere, which is recommended. Remove any files in pg_xlog/. Copy in any latest WAL files from a running server, if any. Add in a recovery.conf and set its file permissions correctly also. Start the server. The only part that requires some thought and checking is which parameters you select for the recovery.conf. There's only one that matters here, and that is the restore_command. restore_command tells us how to restore archived WAL files. It needs to be the command that will be executed to bring back WAL files from the archive. If you are forward-thinking, there'll be a README.backup file for you to read to find out how to set the restore_command. If not, then presumably you've got the location of the WAL files you've been saving written down somewhere. Say, for example, that your files are being saved to a directory named /backups/pg/servername/archive, owned by the postgres user. On a remote server named backup1, we would then write this all on one line of the recovery.conf as follows: restore_command = 'scp backup1:/backups/pg/servername/archive/%f %p' How it works... PostgreSQL is designed to require very minimal information to perform a recovery. We try hard to wrap all the details up for you. Logical recovery: Logical recovery executes SQL to re-create the database objects. If performance is an issue, look at the recipe on recovery performance. Physical recovery: Physical recovery re-applies data changes at the block level so tends to be much faster than logical recovery. Physical recovery requires both a base backup and a set of archived WAL files. There is a file named backup_label in the data directory of the base backup. This tells us to retrieve a .backup file from the archive that contains the start and stop WAL locations of the base backup. Recovery then starts to apply changes from the starting WAL location, and must proceed as far as the stop address for the backup to be valid. After recovery completes, the recovery.conf file is renamed to recovery.done to prevent the server from re-entering recovery. The server log records each WAL file restored from the archive, so you can check progress and rate of recovery. You can query the archive to find out the name of the latest archived WAL file to allow you to calculate how many files to go. The restore_command should return 0 if a file has been restored and non-zero for failure cases. Recovery will proceed until there is no next WAL file, so there will eventually be an error recorded in the logs. If you have lost some of the WAL files, or they are damaged, then recovery will stop at that point. No further changes after that will be applied, and you will likely lose those changes; that would be the time to call your support vendor. There's more... You can start and stop the server once recovery has started without any problem. It will not interfere with the recovery. You can connect to the database server while it is recovering and run queries, if that is useful. That is known as Hot Standby mode. Recovery to a point in time If your database suffers a problem at 15:22 p.m. and yet your backup was taken at 04:00 a.m. you're probably hoping there is a way to recover the changes made between those two times. What you need is known as "point-in-time recovery". Regrettably, if you've made a backup with pg_dump at 04:00 a.m. then you won't be able to recover to any other time than 04:00. As a result, the term point-in-time recovery (PITR) has become synonymous with the physical backup and restore technique in PostgreSQL. Getting ready If you have a backup made with pg_dump, then give up all hope of using that as a starting point for a point in time recovery. It's a frequently asked question, but the answer is still "no"; the reason it gets asked is exactly why I'm pleading with you to plan your backups ahead of time. First, you need to decide what the point of time is that to which you would like to recover. If the answer is "as late as possible", then you don't need to do a PITR at all, just recover until end of logs. How to do it... How do you decide to what point to recover? The point where we stop recovery is known as the "recovery target". The most straightforward way is to do this based upon a timestamp. In the recovery.conf, you can add (or uncomment) a line that says the following: recovery_target_time = '2010-06-01 16:59:14.27452+01' or similar. Note that you need to be careful to specify the time zone of the target, so that it matches the time zone of the server that wrote the log. That might differ from the time zone of the current server, so check. After that, you can check progress during a recovery by running queries in Hot Standby mode. How it works... Recovery works by applying individual WAL records. These correspond to individual block changes, so there are many WAL records to each transaction. The final part of any successful transaction is a commit WAL record, though there are abort records as well. Each transaction completion record has a timestamp on it that allows us to decide whether to stop at that point or not. You can also define a recovery target using a transaction id (xid), though finding out which xid to use is somewhat difficult, and you may need to refer to external records if they exist. The recovery target is specified in the recovery.conf and cannot change while the server is running. If you want to change the recovery target, you can shutdown the server, edit the recovery.conf, and then restart the server. Be careful though, if you change the recovery target and recovery is already passed the point, it can lead to errors. If you define a recovery_target_timestamp that has already passed, then recovery will stop almost immediately, though this will be later than the correct stopping point. If you define a recovery_target_xid that has already passed, then recovery will just continue to the end of logs. Restarting recovery from the beginning using a fresh restore of the base backup is always safe. Once a server completes recovery, it will assign a new "timeline". Once a server is fully available, we can write new changes to the database. Those changes might differ from changes we made in a previous "future history" of the database. So we differentiate between alternate futures using different timelines. If we need to go back and run recovery again, we can create a new server history using the original or subsequent timelines. The best way to think about this is that it is exactly like a Sci-Fi novel—you can't change the past but you can return to an earlier time and take a different action instead. But you'll need to be careful not to confuse yourself. There's more... pg_dump cannot be used as a base backup for a PITR. The reason is that a log replay contains the physical changes to data blocks, not logical changes based upon Primary Keys. If you reload a pg_dump the data will likely go back into different data blocks, so the changes wouldn't correctly reference the data. WAL doesn't contain enough information to reconstruct all SQL fully that produced those changes. Later feature additions to PostgreSQL may add the required information to WAL. See also Planned in 9.1 is the ability to pause/resume/stop recovery, and to set recovery targets while the server is up dynamically. This will allow you to use the Hot Standby facility to locate the correct stopping point more easily. You can trick Hot Standby into stopping recovery, which may help. Recovery of a dropped/damaged table You may drop or even damage a table in some way. Tables could be damaged for physical reasons, such as disk corruption, or they could also be damaged by running poorly specified UPDATEs/DELETEs, which update too many rows or overwrite critical data. It's a common request to recover from this situation from a backup. How to do it... The methods differ, depending upon the type of backup you have available. If you have multiple types of backup, you have a choice. LOGICAL (from custom dump -F c): If you've taken a logical backup using pg_dump into a custom file, then you can simply extract the table you want from the dumpfile like the following: pg_restore -t mydroppedtable dumpfile | psql or connect direct to the database using –d. The preceding command tries to re-create the table and then load data into it. Note that pg_restore -t option does not dump out any of the indexes on the table selected. That means we need a slightly more complex procedure than it would first appear, and the procedure needs to vary depending upon whether we are repairing a damaged table or putting back a dropped table. To repair a damaged table we want to replace the data in the table in a single transaction. There isn't a specific option to do this, so we need to do the following: Dump the table to a script file as follows: pg_restore -t mydroppedtable dumpfile > mydroppedtable.sql Edit a script named restore_mydroppedtable.sql with the following code: BEGIN; TRUNCATE mydroppedtable; i mydroppedtable.sql COMMIT; Then, run it using the following: psql -f restore_mydroppedtable.sql If you've dropped a table then you need to: Create a new database in which to work, name it restorework, as follows: CREATE DATABASE restorework; Restore the complete schema to the new database as follows: pg_restore --schema-only -d restorework dumpfile Now, dump just the definitions of the dropped table into a new file, which will contain CREATE TABLE, indexes, other constraints and grants. Note that this database has no data in it, so specifying –-schema-only is optional, as follows: pg_dump -t mydroppedtable --schema-only restorework > mydroppedtable.sql Now, recreate the table on the main database as follows: psql -f mydroppedtable.sql Now, reload just the data into database maindb as follows pg_restore -t mydroppedtable --data-only -d maindb dumpfile If you've got a very large table, then the fourth step can be a problem, because it builds the indexes as well. If you want you can manually edit the script into two pieces, one before the load ("pre-load") and one after the load ("post-load"). There are some ideas for that at the end of the recipe. LOGICAL (from script dump): The easy way to restore a single table from a script is as follows: Find a suitable server, or create a new virtual server. Reload the script in full, as follows: psql -f myscriptdump.sql From the recovered database server, dump the table, its data, and all the definitions of the dropped table into a new file as follows: pg_dump -t mydroppedtable -F c mydatabase > dumpfile Now, recreate the table into the original server and database, using parallel tasks to speed things along. This can be executed remotely without needing to transfer dumpfile between systems. pg_restore -d mydatabase -j 2 dumpfile The only way to extract a single table from a script dump without doing all of the preceding is to write a custom Perl script to read and extract just the parts of the file you want. That can be complicated, because you may need certain SET commands at the top of the file, the table, and data in the middle of the file, and the indexes and constraints on the table are near the end of the file. It's complex; the safe route is the one already mentioned. PHYSICAL: To recover a single table from a physical backup, we need to: Find a suitable server, or create a new virtual server. Recover the database server in full, as described in previous recipes on physical recovery, including all databases and all tables. You may wish to stop at a useful point in time. From the recovered database server, dump the table, its data, and all the definitions of the dropped table into a new file as follows: pg_dump -t mydroppedtable -F c mydatabase > dumpfile Now, recreate the table into the original server and database using parallel tasks to speed things along. This can be executed remotely without needing to transfer dumpfile between systems as follows: pg_restore -d mydatabase -j 2 dumpfile How it works... At present, there's no way to restore a single table from a physical restore in just a single step. See also Splitting a pg_dump into multiple sections, "pre" and "post" was proposed by me for an earlier release of PostgreSQL, though I haven't had time to complete that yet. It's possible to do that using an external utility also; the best script I've seen to split a dump file into two pieces is available at the following website: http://bucardo.org/wiki/split_postgres_dump
Read more
  • 0
  • 0
  • 5403

article-image-joomla-15-top-extensions-adding-booking-system-events
Packt
25 Oct 2010
4 min read
Save for later

Joomla! 1.5 Top Extensions: Adding a Booking System for Events

Packt
25 Oct 2010
4 min read
Joomla! 1.5 Top Extensions Cookbook Over 80 great recipes for taking control of Joomla! Extensions Set up and use the best extensions available for Joomla! Covers extensions for just about every use of Joomla! Packed with recipes to help you get the most of the Joomla! extensions Part of Packt's Cookbook series: Each recipe is a carefully organized sequence of instructions to complete the task as efficiently as possible Getting ready... The Seminar for Joomla! component allows visitors to register for events. Download this component from http://seminar.vollmar.ws/downloads/com_Seminar_V1.3.0.zip, and install it from the Extensions | Install/Uninstall screen in the Joomla! administration panel. How to do it... Once installed, follow these steps to allow registration for events: From the Joomla! administration panel, select Components | Seminar. It shows the Events screen, listing the available events. To create event categories, click on Categories and then click on New. This shows you the Category: [New] screen. On the Category: [New] screen, type in the Title and Alias, and select Yes in the Published radio box. Then select the Access Level, Image, and Image Position. Enter a description for the category. Click on Save in the toolbar. Repeat this step to add as many categories as you need. Click on Events and then click on New. It will show you the New Event screen. Type the Title of the event. Then select Begin, End, Closing Date, and the time of the event. Select Yes in the Display field if you want to show this information. Type a brief description of the event, and the place of the event. Select an organizer from the Organiser drop-down list. Then type the maximum number of participants in the Max. Particip. field. Also, select what to do when the number of participants exceeds this number. When this is done, click on Additional Settings. This shows you the Additional Settings screen. In the Additional Settings section, you can add an additional description. In the Description box, add descriptions to particular groups of visitors. The syntax to add messages for different groups is shown above the text box. Select an image for the event overview. Then type the tutor's name, target group, and the fees per person. Then click on General Input Fields. This will show the following screen: In the General Input Fields section, you can include additional fields. To add additional fields, follow the syntax provided. When this is done, click on Files. This shows the following screen for file upload. Click on the Browse button and select the file to upload. Type a description of the file and select the group that can view it. When this is done, click on the Save button in the toolbar. Select Menus | Main Menu, and then click on New. This will show you the Menu Item: [New] wizard. In the Select Menu Item Type section, select Seminar and provide a title for the menu item on the next screen. Then save the menu item. Preview the site's frontend, as a user logged in to the frontend, and click on the menu item for the seminar. This will display the following screen showing your added event. The overview of the event shows the overview image, event title, category, reservation information, and an indicator for the booking status. Click on the event title, it will show you the event details, as shown in the following screenshot: Select the spaces to be booked. Then type your Name and Email address and click on the Book button. This books your space for the event. You can view your booking by clicking the My Bookings tab, as shown in the following screenshot. Note that your participation status is indicated through a color-coded indicator. There's more... Most of the features of Seminar for the Joomla! component can be configured from the Settings screen. For example, you can configure who can book events, what happens when booking exceeds the number of seats, who can rate the events from the frontend, which folder will store images of the events, the maximum file upload size, the file extensions allowed for uploads, and so on. Summary This article showed you how to allow visitors to register for an event. Further resources on this subject: Adding an Event Calendar to your Joomla! Site using JEvents Showing your Google calendar on your Joomla! site using GCalendar Joomla! 1.5 Top Extensions for Using Languages Manually Translating Your Joomla! Site's Content into Your Desired Language
Read more
  • 0
  • 0
  • 2471

article-image-microsoft-wcf-hosting-and-configuration
Packt
25 Oct 2010
7 min read
Save for later

Microsoft WCF Hosting and Configuration

Packt
25 Oct 2010
7 min read
Service hosting and configuration is very important for building WCF services, especially at the service deployment stage. After developers complete the service development, we will need to deploy the service so as to make it available to all the client consumers. In the real world, there are various service deployment scenarios available, which will result in different deployment and configuration requirements on the service configuration or the hosting environment. As an enhanced service development platform, WCF provides rich, built-in support on service hosting and configuration that can fulfill most of the existing deployment demands and requirements. For example, the most popular IIS hosting approach can provide high availability and stable service for local intranet or public internet-based deployment cases. The Windows service-hosting approach makes WCF service hosting easier to integrate with existing background scheduled tasks, and the self-hosting approach provides the most flexibility and customization points for service deployment in a production environment. In this article, we will look at seven recipes on various WCF hosting and configuration scenarios. The recipes start with four typical hosting cases—self-hosting, Windows service hosting, IIS-based HTTP hosting, and IIS based non-HTTP hosting. This is followed by two customized service-hosting cases—including a custom ServiceHostFactory and a dedicated singleton-instance hosting. The last recipe demonstrates a more advanced WCF service-hosting scenario—Windows SharePoint Service hosting. Hosting a service in a console application When creating a simple demo program for .NET framework, we will probably choose a console application. At the same, when talking about WCF service hosting, the console-hosting scenario is the most convenient one, which is especially handy and useful when we want to do some quick demo or testing on some WCF functionality. How to do it... Create a .NET framework-based Console project through Visual Studio. Visual Studio provides various project templates for creating a .NET framework-based application. For our sample console-hosting service here, we will choose the Console Application project type from the Visual Studio New Project wizard. Add a new WCF service into the project. We can simply accomplish this by using the Add New Item function in Visual Studio and choose WCF Service as the item type from Visual Studio's Add New Item UI. Add code into the Main function to start up the WCF service. The following code shows the typical Main function that starts up a WCF service in a console application: static void Main(string[] args) { using (ServiceHost consoleHost = new ServiceHost(typeof(TestService))) { consoleHost.Open(); Console.WriteLine("press any key to stop service host..."); Console.ReadLine(); } } How it works... When you add a new WCF Service item in Visual Studio, the IDE actually helps you to finish the following three tasks: Creating a ServiceContract interface. Creating a Service class that implements the ServiceContract interface. The following code shows the sample ServiceContract and implementation class used in this recipe. [ServiceContract] public interface ITestService { [OperationContract] void DoWork(); } public class TestService : ITestService { public void DoWork() { } } Adding the service endpoint and binding configuration in the App.config file. In addition to the Contract and service type, the IDE will also insert a default configuration setting for the endpoint that can be exposed through the service. The following screenshot shows the sample service configuration section that contains a single endpoint, which uses WSHttpBinding. With the code and configuration entries as defined previously, we can start our service host by supplying the service type in the constructor of the ServiceHost class. using (ServiceHost consoleHost = new ServiceHost(typeof(TestService))) What the runtime will do is, it will lookup the configuration file and load the <service> entry that has the name identical to the type specified in the constructor, and launch the service and endpoints defined in it. The source code for this article can be found at http://www.packtpub.com/code_download/6034 Hosting a service in Windows Service Windows Services are widely used on Windows operating systems for hosting applications that will perform some long-run or scheduled tasks in the background. Applications hosted via Windows Service can be running under a specific user account and can choose the startup mode (manually or automatically). As a popular service-application-hosting scenario, it is also quite common to deploy a WCF service as a Windows Service. How to do it... In this recipe, we will use a typical .NET-based Windows Service to demonstrate how to host a WCF service in a Windows Service application. Let's go through the detailed steps: Create a Windows Service project. The first step is to create a new Windows Service project through the Visual Studio IDE. When creating the project, we simply choose the Windows Service project type. The following screenshot shows how we can select the Windows Service project type in the Visual Studio New Project wizard. Add a new WCF service item. As a WCF service hosting application, we certainly need to have a WCF service defined here. The steps for creating a WCF service are the same as what we've discussed in the Hosting a service in console application recipe. Add service hosting code into the service startup and shutdown event. As for the service-hosting code in the Windows Service, we need to put it in the correct place, since the .NET-based Windows Service type doesn't directly expose the Main function. The following code shows how the WCF service startup and shutdown code is defined: public partial class Service1 : ServiceBase { ServiceHost _svcHost = null; protected override void OnStart(string[] args) { // Start the service host here _svcHost = new ServiceHost(typeof(TestService)); _svcHost.Open(); } protected override void OnStop() { // Close the service host _svcHost.Close(); } } Add an installer for the Windows Service. Now the Windows Service class and WCF service types have been defined. However, we still need to add another component—the installer class for deploying the Windows Service into the Windows Service collection on the target operating system. In the Visual Studio IDE, we can simply add an installer type for the Windows Service by the context menu on the component designer. The following screenshot shows the context menu item for creating the installer class for the Windows Service. The IDE will help create two helper classes—one is of ServiceProcessInstaller type and another of ServiceInstaller type. We can specify many deployment parameters for the Windows Service in the Property panel of the two classes. The following screenshot shows the properties of the sample serviceProcessInstaller1 class. The next screenshot shows the properties of the sample serviceInstaller1 class. As with the screenshots displayed, Visual Studio will use standard Properties windows for displaying and configuring the individual properties of the Windows Service classes. Install the Windows Service through Installutil.exe. The last step is to install the Windows Service we have created (after building the project) into the operating system. This can be done by using the Installutil.exe tool provided by the .NET framework. You can directly execute the Installutil.exe command within the Visual Studio command-line prompt window or you can choose to launch the tool through its absolute path in the .NET framework folder such as C: WindowsMicrosoft.NETFrameworkv4.0.30319. The following statements show the complete commands for installing and uninstalling a .NET-based Windows Service application via the Installutil. exe tool. Install the Windows Service: InstallUtil.exe WCFNTService.exe Uninstall the Windows Service: Install Util.exe /u WCFNTService.exe The WCFNTService.exe mentioned earlier is the output assembly name of the sample Windows Service project. How it works... The OnStart event is fired when the Windows Service is starting, while the OnStop event is fired when the Windows Service is shutting down. Therefore, they are the best places for us to put the WCF service-hosting code. Sometimes, we may need to access some remote or protected resource in our Windows Service host program. In such cases, it is important to specify a proper service account, either at development time or in the Windows Service Configuration Manager. The following screenshot shows the service list, which contains the installed sample Windows Service in Windows Service Configuration Manager.
Read more
  • 0
  • 0
  • 2951
article-image-microsoft-dynamics-nav-2009-designing-forms
Packt
25 Oct 2010
8 min read
Save for later

Microsoft Dynamics NAV 2009: Designing Forms

Packt
25 Oct 2010
8 min read
Forms are a predominant visual element in Dynamics NAV. There are 937 tables in the base NAV software and 1,820 forms that display information from those tables. Apart from learning how to create a form using the wizard, this article will not discuss the basic elements of form design. That information can be found in the C/SIDE Reference Guide and Development Coursework from Microsoft. If you have not designed a form before, it is highly recommended that you acquaint yourself with forms first. With NAV 2009, Microsoft released the RoleTailored client, or RTC. This was a huge change from the existing NAV product. In this release, Microsoft introduced the RTC as a second client or interface in addition to what is called the Classic client, or more traditional interface. While the future of NAV is definitely with the RTC, it is still important to understand what forms are and how they work, in order to support customers who might not upgrade to the latest version of the product. Obtaining input without a form Sometimes you don't want to use an entire form to get user input. Dialog boxes are not a substitute for forms, but they work just fine for quick input. How to do it... Create a new codeunit from Object Designer. Add the following global variables: Add the following code to the OnRun trigger of the codeunit: Window.OPEN('Customer No: #1####################'); Window.INPUT(1, CustomerNo); Window.CLOSE; IF Customer.GET(CustomerNo) THEN MESSAGE('Customer Name: %1', Customer.Name) ELSE MESSAGE('No customer found!); Save and close the codeunit. How it works... The first line of code opens an input dialog window that looks like one shown in the following screenshot: The next line lets the user input a value and stores it in the CustomerNo variable. The dialog window then closes and the result can be used later in code. There's more... As you can tell from the input window, dialogs are much weaker than forms when it comes to functionality. You can't do lookups, data validation, or anything other than basic text input. From a licensing aspect, forms are one of the cheapest objects to buy. They also don't match the look and feel for the rest of the system. For these reasons it is almost always better to use a form than an input dialog, but it is important to know what you can do using dialogs. Using the Form Generation Wizard You can always create a form manually, but using the Form Generation Wizard is a quick and painless way to create the skeleton. How to do it... With the form selected in Object Designer click the New button. Choose the Customer table. Select Create a form using a wizard. Select Tabular-Type Form. Click OK. Use the arrow buttons between the two lists to add the No. and Name fields. Click on Finish. How it works... The Form Generation Wizard allows you to tell the system what fields you want on the form and the format or order in which you want them to appear. NAV will then automatically place the fields on the form for you. There is no manual positioning of labels or textboxes; no creating tabs or list boxes. It is all done automatically. There's more... The wizard will only create a basic form for you. If you need to create special functions or do any specific data validation, you will have to code that manually. A wizard is only designed to get you started, not to do anything advanced. Changing text appearance A great way to improve the user experience is to change the way text appears on the screen. This recipe will explore several options that are available to you. Getting ready Design the Customer List form and save it as a new object. How to do it... Design the copy of the Customer List form. Create a function named GetColor that returns an integer. Add the following code to the function: IF "Location Code" = 'BLUE' THEN EXIT(16711680) ELSE IF "Location Code" = 'GREEN' THEN EXIT(65280) ELSE IF "Location Code" = 'RED' THEN EXIT(255) ELSE IF "Location Code" = 'YELLOW' THEN EXIT(65535) Create a function named GetBold that returns a boolean value. Add the following code to the function: EXIT("Credit Limit (LCY)" > 1000); In the OnFormat trigger for the name column, add the following code: CurrForm.Name.UPDATEFORECOLOR(GetColor); CurrForm.Name.UPDATEFONTBOLD(GetBold); Save and close the form. How it works... The trigger that controls the appearance of text is the OnFormat trigger. The first function we use is UPDATEFORECOLOR. This method is found on every text field in a form. It takes one parameter—the color we want the text to be. In our example, we pass a function as the parameter and that function returns the color we should use. UPDATEFONTBOLD works in a similar way. It takes a boolean parameter that tells the form whether or not to emphasize the text. The resulting form will look similar to the one shown in the following screenshot: There's more... The look and feel of a system is important for user satisfaction. Finding ways to make the information easier to understand, such as displaying the text in the same color as the warehouse location, can improve user understanding and decrease the time it takes to look up information. That said, don't go overboard. Having a form with multiple colors that have no direct relation to the data can be confusing. You don't want to the user to have a "cheat sheet" of what everything means. If it takes longer than a couple of minutes to explain what certain characteristics mean and you can't remember them an hour later, then you probably have gone too far. It also makes your upgrade-time to the RoleTailored client longer because display colors only have limited support. Preventing editable lookup forms You may want users to only add records when running a form from a setup location. This example will show you how to prevent users from adding or modifying values when only trying to look up a record. Getting ready This example will use the Salesperson/Purchasers form (14). How to do it... Design the Salesperson/Purchasers form from Object Designer. In the OnOpen trigger for the form, add the following code: IF CurrForm.LOOKUPMODE THEN CurrForm.EDITABLE := FALSE; Save and close the form. How it works... The code here is pretty self-explanatory. If the form is in lookup mode, it will not be editable. There's more... The Lookup mode is a special mode in which forms can run. Essentially, when in lookup mode, the OK and Cancel buttons are displayed; when not in lookup mode, they are hidden. When using these buttons you can retrieve the selected value from the form. It is often a good idea to make forms uneditable in lookup mode, although you will find many forms in base NAV where this is not the case. When the purpose of running a form is only to retrieve a value, it is a good idea to make sure that the form is not editable to make sure those values are not accidentally changed. Adding an editable field to a non-editable form Have you ever needed to make a form uneditable rather than just one field? This recipe will show you a quick and easy way to do it. Getting ready Create a list form based on the Customer table that displays the number and name of the customer. The Editable property of the form should be set to No. How to do it... View the code for the Name column in the list form. In the OnActivate trigger, add the following code: CurrForm.EDITABLE := TRUE; In the OnDeactivate trigger add the following code: CurrForm.EDITABLE := FALSE; Save and close the form. How it works... When you click on a textbox its OnActivate trigger is executed. In our form, we have told the system to override the default Editable property when we click on the textbox. We set it to true so that the field becomes editable. In fact, the entire form becomes editable. We must make the entire form editable because that overrides the editable property of the controls on the form. But when we click-off or tab-off of the field the OnDeactivate trigger fires. We then reset the form back to uneditable. Whenever the field is activated you can edit it, otherwise you cannot edit anything. In the RoleTailored client there is no OnActivate or OnDeactivate trigger. You will have to do it the hard way, that is, by setting the Editable property on every field. Further resources on this subject: Microsoft Dynamics NAV 2009: Creating a Matrix Form Microsoft Dynamics NAV 2009: Creating a Wizard-style Form
Read more
  • 0
  • 0
  • 8358

article-image-omnigraffle-5-shape-selection-re-styling-and-color-picker-detail
Packt
25 Oct 2010
8 min read
Save for later

OmniGraffle 5: Shape Selection, Re-Styling and Color Picker in Detail

Packt
25 Oct 2010
8 min read
OmniGraffle 5 Diagramming Essentials Create better diagrams with less effort using OmniGraffle Produce high-quality professional-looking diagrams that communicate information much better than words Makes diagramming fun and simple for Macintosh users Master the art of illustrating your ideas with OmniGraffle Learn to draw engaging charts and graphs to grasp your viewers' attention to your presentations A hands-on guide filled with visual step-by-step examples that cover both the basics and the advanced features of OmniGraffle        Easy shape selection When diagrams become complex and you want to change the appearance of many of the same objects, you can either hold down the shift key on your keyboard and select the shapes you need to change, or you can use the built-in selection functions in OmniGraffle. There are three built in methods of selecting shapes: The Edit | Select | Similar Objects menu command. The context sensitive menu when right-clicking on selected shapes (Select | Similar Objects). Using the Canvas: Selection inspector. If shapes are connected to each other, it's also possible to select connected shapes from the application menu or the context sensitive menu. You will deal with connected shapes later. What OmniGraffle defines as similar shapes Similar shapes are shapes that have exactly the same styling, not the form, type or size of the shapes. The styling can be; the fi lling color or blend of the shape, the stroke thickness, the corner radius, the stroke color, the shadow, if the shape is filled with a picture and if the shape has an associated text. Neither the size of two or more shapes, nor the content of the text will make OmniGraffle define the shapes as dissimilar. If you change the font type, weight or color–then OmniGraffle will define the shapes as dissimilar. As the few examples in the table below show more often than not, shapes that are of the same type (rectangle, circle, and so on) are dissimilar. ShapesExplanationResultSame font, color and weight, but different text.Similar shapes.Difference in the fuzziness of the shadow.Dissimilar shapes.Different font size.Dissimilar shapes.Different color. Different text, but same font, color and weight.Dissimilar shapes. Let's explore how easy it is to select similar shapes. Create an OmniGraffle document with only 4 shapes: Rectangle, Circle, Diamond, and Triangle. Mix them all together so it's not easy to manually select various shapes. In the download bundle, Ch:7, there is a file named Experiments in shape selections.graffle—you can open this if you do not want to create your own version. In the Canvas: Selection inspector (seen in the previous screenshot), you will see the four different shapes. Under each shape, there are two numbers separated by a forward slash (/). The number on the left indicates how many of the given shape are selected, and the number to the right of the slash is the total count of shapes on the current canvas. As you can see in the Selection inspector, we have 11 rectangles, 9 triangles, 10 circles, and 10 diamonds. To select all the diamonds on the canvas, click on the diamond shape in the Selection inspector. Notice that the numbers below the shape are now reading 10/10. If you click on the Fill style inspector and use the color named Honeydew from the Crayon color palette—all the diamonds change into this color. Now, click on the rectangle in the upper-left corner and right-click to get the context sensitive menu. Then select then use the Select | Similar Objects menu command, as seen next: Now all the rectangles are selected. Fill the rectangles with the color named Tin from the Crayons color palette. The next thing you are going to do is to select four of the circles and fill these with a yellow color. Notice that the Selection inspector now has five different shapes: You can now continue on your own to experiment changing various style properties to the shape groups. Selecting connected shapes You are now going to perform some experiments with connected shapes. Start with a diagram like the one next. You can either create this by hand, or you can open the file named Experiments in shape selections.graffle found in the Ch:7 directory in the download bundle. The diagram is found on the second canvas. Notice that all shapes are connected to each other. If you look at the Selection inspector, you'll see four shapes, where the lines are one of those shapes. OmniGraffle defines descendants to a selected shape as all shapes that are either connected to the right of or below the selected shape. OmniGraffle defines ancestors to a selected shape as all shapes that are either connected to the left of or above the selected shape. The following table lists a few actions and their corresponding results while working with connected shapes. ActionResultChoose (select) circle number 1 and execute Descendants from the Edit | Selection menu. This action results in the selection of the shapes connected below and to the right of the chosen shape.Select circle number 2 and choose Ancestors from the Edit | Selection menu. This action results in the selection of shapes connected to the left of the chosen shape.Select circle number 3 and choose Ancestors from the Edit | Selection menu. This action results in the selection of shapes connected above and to the left of the chosen shape.Select square B and choose Descendants from the Edit | Selection menu. This action result in every shape except circle number 1 and square A being selected.Select square C and choose Ancestors from the Edit | Selection menu. The shapes connected to the left of the chosen shape are selected.Select square C and choose Descendants from the Edit | Selection menu. The shapes connected below and to the right of the chosen shape are selected.Select any shape and choose Connected Objects from the Edit | Selection menu. This action results in the whole diagram being selected since every shape is connect to every other shape, either directly or indirectly. Selecting all these shapes—either by form or by connections seems like a very powerful tool, and indeed it is. With this way of selecting shapes, you can easily change the look and feel in a consistent manner throughout your whole diagram. Easy re-styling of shapes Instead of selecting similar shapes, and then changing their appearance by using the style inspectors, you can also drag and drop styles from the Style Tray directly onto the Selection inspector. Start your experiments in the easy re-styling of shapes by creating various shapes, and fill some of these with various colors. To save you some time and get right into the experimentation, you can also open the file named Experiments in re-styling shapes.graffle, which is found in the Ch: 6 folder of the download bundle. We're not quite happy with only having four yellow circles. Your first task is to make the rest of the circles yellow. Unfortunately, you do not know which yellow color these circles are – you could work with the color picker to get the right yellow. This is too much work. Let's do this the simple way. In the Selection inspector, you will see all the different shapes, including the four yellow circles. If you use the file from the download bundle, you should have the yellow circles as the first shape in the inspector. To select all the yellow circles, you could click on one of the circles and then use the Edit | Select | Similar Objects menu command. This is too much work really. Just click on the shape inside the inspector. What happens now is that all the yellow circles have been selected. In the Style Tray, the styling for these shapes is now shown: You can now drag the color chit (//Image 43) on to the white circle in the Selection inspector. Suddenly all your circles have this shade of yellow. And it's not only the color we can change in a second like this. In fact, your next job is to eradicate all of those gray and boring squares and instead replace these with the green and happy diamonds. This is also just a two-step process. Start by clicking on one of the diamonds in your canvas. Now, drag the Complete Style Chit (the leftmost of all the chits) onto the gray squares inside the selection inspector. Not only does this change the color of the square shapes— but it also changes the shapes into diamonds. You can thus drag any chit from the Style Tray onto any shapes both on the canvas , and also onto the Selection inspector. This is a very powerful tool to aid you in getting a consistent look for your shapes. You can also use this method to search and replace a lot of shapes in one go. No need to manually hunt around on a shape-by-shape basis.
Read more
  • 0
  • 0
  • 5510
Modal Close icon
Modal Close icon