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

How-To Tutorials

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

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

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

article-image-creating-administration-interface-django
Packt
16 Oct 2009
5 min read
Save for later

Creating an Administration Interface in Django

Packt
16 Oct 2009
5 min read
Activating the Administration Interface The administration interface comes as a Django application. To activate it, we will follow a simple procedure that is similar to how we enabled the user authentication system. The admininistration application is located in the django.contrib.admin package. So the first step is adding the path of this package to the INSTALLED_APPS variable. Open settings.py, locate INSTALLED_APPS and edit it as follows: INSTALLED_APPS = ( 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.sites', 'django.contrib.admin', 'django.contrib.comments', 'django_bookmarks.bookmarks',) Next, run the following command to create the necessary tables for the administration application: $ python manage.py syncdb Now we need to make the administration interface accessible from within our site by adding URL entries for it. The administration application defines many views, so manually adding a separate entry for each view can become a tedious task. Therefore, Django provides a shortcut for this. The administration interface defines all of its URL entries in a module located at django.contrib.admin.urls, and we can include these entries in our project under a particular path by using a function called include(). Open urls.py and add the following URL entry to it: urlpatterns = ( # Admin interface (r'^admin/', include('django.contrib.admin.urls')),) This looks different from how we usually define URL entries. We are basically telling Django to retrieve all of the URL entries in the django.contrib.admin.urls module, and to include them in our application under the path ^admin/. This will make the views of the administration interface accessible from within our project. One last thing remains before we see the administration page in action. We need to tell Django what models can be managed in the administration interface. This is done by defining a class called Admin inside each model. Open bookmarks/models.py and add the highlighted section to the Link model: class Link(models.Model): url = models.URLField(unique=True) def __str__(self): return self.url class Admin: pass The Admin class defined inside the model effectively tells Django to enable the Link model in the administration interface. The keyword pass means that the class is empty. Later, we will use this class to customize the administration page, so it won't remain empty. Do the same to the Bookmark, Tag and SharedBookmark models; append an empty class called Admin to each of them. The User model is provided by Django and therefore we don't have control over it. Fortunately however, it already contains an Admin class so it's available in the administration interface by default. Next, launch the development server and direct your browser to http://127.0.0.1:8000/admin/. You will be greeted by a login page. Remember we need to create a superuser account after writing the database model. This is the account that you have to use in order to log in: Next, you will see a list of the models that are available to the administration interface. As discussed earlier, only models with a class named Admin inside them will appear on this page: If you click on a model name, you will get a list of the objects that are stored in the database under this model. You can use this page to view or edit a particular object, or to add a new one. The figure below shows the listing page for the Link model. The edit form is generated according to the fields that exist in the model. The Link form, for example, contains a single text field called Url. You can use this form to view and change the URL of a Link object. In addition, the form performs proper validation of fields before saving the object. So if you try to save a Link object with an invalid URL, you will receive an error message asking you to correct the field. The figure below shows a validation error when trying to save an invalid link: Fields are mapped to form widgets according to their type. Date fields are edited using a calendar widget for example, whereas foreign key fields are edited using a list widget, and so on. The figure below shows a calendar widget from the user edit page. Django uses it for date and time fields: As you may have noticed, the administration interface represents models by using the string returned by the __str__ method. It was indeed a good idea to replace the generic strings returned by the default __str__ method with more helpful ones. This greatly helps when working with the administration page, as well as with debugging. Experiment with the administration pages; try to create, edit and delete objects. Notice how changes made in the administration interface are immediately reflected on the live site. Also, the administration interface keeps track of the actions that you make, and lets you review the history of changes for each object. This section has covered most of what you need to know in order to use the administration interface provided by Django. This feature is actually one of the main advantages of using Django; you get a fully featured administration interface from writing only a few lines of code! Next, we will see how to tweak and customize the administration pages. And as a bonus, we will learn more about the permissions system offered by Django.
Read more
  • 0
  • 0
  • 3073

article-image-drools-jboss-rules-50-flow-part-1
Packt
16 Oct 2009
10 min read
Save for later

Drools JBoss Rules 5.0 Flow (Part 1)

Packt
16 Oct 2009
10 min read
Loan approval service Loan approval is a complex process starting with customer requesting a loan. This request comes with information such as amount to be borrowed, duration of the loan, and destination account where the borrowed amount will be transferred. Only the existing customers can apply for a loan. The process starts with validating the request. Upon successful validation, a customer rating is calculated. Only customers with a certain rating are allowed to have loans. The loan is processed by a bank employee. As soon as an approved event is received from a supervisor, the loan is approved and money can be transferred to the destination account. An email is sent to inform the customer about the outcome. Model If we look at this process from the domain modeling perspective, in addition to the model that we already have, we'll need a Loan class. An instance of this class will be a part of the context of this process. The screenshot above shows Java Bean, Loan, for holding loan-related information. The Loan bean defines three properties. amount (which is of type BigDecimal), destinationAccount (which is of type Account; if the loan is approved, the amount will be transferred to this account), and durationYears (which represents a period for which the customer will be repaying this loan). Loan approval ruleflow We'll now represent this process as a ruleflow. It is shown in the following figure. Try to remember this figure because we'll be referring back to it throughout this article. The preceding figure shows the loan approval process—loanApproval.rf file. You can use the Ruleflow Editor that comes with the Drools Eclipse plugin to create this ruleflow. The rest of the article will be a walk through this ruleflow explaining each node in more detail. The process starts with Validate Loan ruleflow group. Rules in this group will check the loan for missing required values and do other more complex validation. Each validation rule simply inserts Message into the knowledge session. The next node called Validated? is an XOR type split node. The ruleflow will continue through the no errors branch if there are no error or warning messages in the knowledge session—the split node constraint for this branch says: not Message() Code listing 1: Validated? split node no errors branch constraint (loanApproval.rf file). For this to work, we need to import the Message type into the ruleflow. This can be done from the Constraint editor, just click on the Imports... button. The import statements are common for the whole ruleflow. Whenever we use a new type in the ruleflow (constraints, actions, and so on), it needs to be imported. The otherwise branch is a "catch all" type branch (it is set to 'always true'). It has higher priority number, which means that it will be checked after the no errors branch. The .rf files are pure XML files that conform with a well formed XSD schema. They can be edited with any XML editor. Invalid loan application form If the validation didn't pass, an email is sent to the customer and the loan approval process finishes as Not Valid. This can be seen in the otherwise branch. There are two nodes-Email and Not Valid. Email is a special ruleflow node called work item. Email work item Work item is a node that encapsulates some piece of work. This can be an interaction with another system or some logic that is easier to write using standard Java. Each work item represents a piece of logic that can be reused in many systems. We can also look at work items as a ruleflow alternative to DSLs. By default, Drools Flow comes with various generic work items, for example, Email (for sending emails), Log (for logging messages), Finder (for finding files on a file system), Archive (for archiving files), and Exec (for executing programs/system commands). In a real application, you'd probably want to use a different work item than a generic one for sending an email. For example, a custom work item that inserts a record into your loan repository. Each work item can take multiple parameters. In case of email, these are: From, To, Subject, Text, and others. Values for these parameters can be specified at ruleflow creation time or at runtime. By double-clicking on the Email node in the ruleflow, Custom Work Editor is opened (see the following screenshot). Please note that not all work items have a custom editor. In the first tab (not visible), we can specify recipients and the source email address. In the second tab (visible), we can specify the email's subject and body. If you look closer at the body of the email, you'll notice two placeholders. They have the following syntax: #{placeholder}. A placeholder can contain any mvel code and has access to all of the ruleflow variables (we'll learn more about ruleflow variables later in this article). This allows us to customize the work item parameters based on runtime conditions. As can be seen from the screenshot above, we use two placeholders: customer.firstName and errorList. customer and errorList are ruleflow variables. The first one represents the current Customer object and the second one is ValidationReport. When the ruleflow execution reaches this email work item, these placeholders are evaluated and replaced with the actual values (by calling the toString method on the result). Fault node The second node in the otherwise branch in the loan approval process ruleflow is a fault node. Fault node is similar to an end node. It accepts one incoming connection and has no outgoing connections. When the execution reaches this node, a fault is thrown with the given name. We could, for example, register a fault handler that will generate a record in our reporting database. However, we won't register a fault handler, and in that case, it will simply indicate that this ruleflow finished with an error. Test setup We'll now write a test for the otherwise branch. First, let's set up the test environment. Then a new session is created in the setup method along with some test data. A valid Customer with one Account is requesting a Loan. The setup method will create a valid loan configuration and the individual tests can then change this configuration in order to test various exceptional cases. @Before public void setUp() throws Exception { session = knowledgeBase.newStatefulKnowledgeSession(); trackingProcessEventListener = new TrackingProcessEventListener(); session.addEventListener(trackingProcessEventListener); session.getWorkItemManager().registerWorkItemHandler( "Email", new SystemOutWorkItemHandler()); loanSourceAccount = new Account(); customer = new Customer(); customer.setFirstName("Bob"); customer.setLastName("Green"); customer.setEmail("bob.green@mail.com"); Account account = new Account(); account.setNumber(123456789l); customer.addAccount(account); account.setOwner(customer); loan = new Loan(); loan.setDestinationAccount(account); loan.setAmount(BigDecimal.valueOf(4000.0)); loan.setDurationYears(2); Code listing 2: Test setup method called before every test execution (DefaulLoanApprovalServiceTest.java file). A tracking ruleflow event listener is created and added to the knowledge session. This event listener will record the execution path of a ruleflow—store all of the executed ruleflow nodes in a list. TrackingProcessEventListener overrides the beforeNodeTriggered method and gets the node to be executed by calling event.getNodeInstance(). loanSourceAccount represents the bank's account for sourcing loans. The setup method also registers an Email work item handler. A work item handler is responsible for execution of the work item (in this case, connecting to the mail server and sending out emails). However, the SystemOutWorkItemHandler implementation that we've used is only a dummy implementation that writes some information to the console. It is useful for our testing purposes. Testing the 'otherwise' branch of 'Validated?' node We'll now test the otherwise branch, which sends an email informing the applicant about missing data and ends with a fault. Our test (the following code) will set up a loan request that will fail the validation. It will then verify that the fault node was executed and that the ruleflow process was aborted. @Test public void notValid() { session.insert(new DefaultMessage()); startProcess(); assertTrue(trackingProcessEventListener.isNodeTriggered( PROCESS_LOAN_APPROVAL, NODE_FAULT_NOT_VALID)); assertEquals(ProcessInstance.STATE_ABORTED, processInstance.getState()); } Code listing 3: Test method for testing Validated? node's otherwise branch (DefaultLoanApprovalServiceTest.java file). By inserting a message into the session, we're simulating a validation error. The ruleflow should end up in the otherwise branch. Next, the test above calls the startProcess method. It's implementation is as follows: private void startProcess() { Map<String, Object> parameterMap = new HashMap<String, Object>(); parameterMap.put("loanSourceAccount", loanSourceAccount); parameterMap.put("customer", customer); parameterMap.put("loan", loan); processInstance = session.startProcess( PROCESS_LOAN_APPROVAL, parameterMap); session.insert(processInstance); session.fireAllRules(); } Code listing 4: Utility method for starting the ruleflow (DefaultLoanApprovalServiceTest.java file). The startProcess method starts the loan approval process. It also sets loanSourceAccount, loan, and customer as ruleflow variables. The resulting process instance is, in turn, inserted into the knowledge session. This will enable our rules to make more sophisticated decisions based on the state of the current process instance. Finally, all of the rules are fired. We're already supplying three variables to the ruleflow; however, we haven't declared them yet. Let's fix this. Ruleflow variables can be added through Eclipse's Properties editor as can be seen in the following screenshot (just click on the ruleflow canvas, this should give the focus to the ruleflow itself). Each variable needs a name type and, optionally, a value. The preceding screenshot shows how to set the loan ruleflow variable. Its Type is set to Object and ClassName is set to the full type name droolsbook.bank.model.Loan. The other two variables are set in a similar manner. Now back to the test from code listing 3. It verifies that the correct nodes were triggered and that the process ended in aborted state. The isNodeTriggered method takes the process ID, which is stored in a constant called PROCESS_LOAN_APPROVAL. The method also takes the node ID as second argument. This node ID can be found in the properties view after clicking on the fault node. The node ID—NODE_FAULT_NOT_VALID—is a constant of type long defined as a property of this test class. static final long NODE_FAULT_NOT_VALID = 21;static final long NODE_SPLIT_VALIDATED = 20; Code listing 5: Constants that holds fault and Validated? node's IDs (DefaultLoanApprovalServiceTest.java file). By using the node ID, we can change node's name and other properties without breaking this test (node ID is least likely to change). Also, if we're performing bigger re-factorings involving node ID changes, we have only one place to update—the test's constants. Ruleflow unit testingDrools Flow support for unit testing isn't the best. With every test, we have to run the full process from start to the end. We'll make it easier with some helper methods that will set up a state that will utilize different parts of the flow. For example, a loan with high amount to borrow or a customer with low rating.Ideally we should be able to test each node in isolation. Simply start the ruleflow in a particular node. Just set the necessary parameters needed for a particular test and verify that the node executed as expected.Drools support for snapshots may resolve some of these issues; however, we'd have to first create all snapshots that we need before executing the individual test methods. Another alternative is to dig deeper into Drools internal API, but this is not recommended. The internal API can change in the next release without any notice.
Read more
  • 0
  • 0
  • 2831

article-image-jbi-binding-components-netbeans-ide-6
Packt
16 Oct 2009
4 min read
Save for later

JBI Binding Components in NetBeans IDE 6

Packt
16 Oct 2009
4 min read
Binding Components Service Engines are pluggable components which connect to the Normalized Message Router (NMR) to perform business logic for clients. Binding components are also standard JSR 208 components that plug in to NMR and provide transport independence to NMR and Service Engines. The role of binding components is to isolate communication protocols from JBI container so that Service Engines are completely decoupled from the communication infrastructure. For example, BPEL Service Engine can receive requests to initiate BPEL process while reading files on the local file system. It can receive these requests from SOAP messages, from a JMS message, or from any of the other binding components installed into JBI container. Binding Component is a JSR 208 component that provides protocol independent transport services to other JBI components. The following figure shows how binding components fit into the JBI Container architecture: In this figure, we can see that the role of BC is to send and receive messages both internally and externally from Normalized Message Router using protocols, specific to the binding component. We can also see that any number of binding components can be installed into the JBI container. This figure shows that like Service Engines (SE), binding components do not communicate directly with other binding components or with Service Engines. All communication between individual binding components and between binding components and Service Engines is performed via sending standard messages through the Normalized Message Router. NetBeans Support for Binding Components The following table lists which binding components are installed into the JBI container with NetBeans 5.5 and NetBeans 6.0:   As is the case with Service Engines, binding components can be managed within the NetBeans IDE. The list of Binding Components installed into the JBI container can be displayed by expanding the Servers | Sun Java System Application Server 9 | JBI | Binding Components node within the Services explorer. The lifecycle of binding components can be managed by right-clicking on a binding component and selecting a lifecycle process—Start, Stop, Shutdown, or Uninstall. The properties of an individual binding component can also be obtained by selecting the Properties menu option from the context menu as shown in the following figure. Now that we've discussed what binding components are, and how they communicate both internally and externally to the Normalized Message Router, let's take a closer look at some of the more common binding components and how they are accessed and managed from within the NetBeans IDE. File Binding Component The file binding component provides a communications mechanism for JBI components to interact with the file system. It can act as both a Provider by checking for new files to process, or as a Consumer by outputting files for other processes or components. The figure above shows the file binding component acting as a Provider of messages. In this scenario, a message has been sent to the JBI container, and picked up by a protocol-specific binding component (for example, a SOAP message has been received). A JBI Process then occurs within the JBI container which may include routing the message between many different binding components and Service Engines depending upon the process. Finally, after the JBI Process has completed, the results of the process are sent to File Binding Component which writes out the result to a file. The figure above shows the file binding component acting as a Consumer of messages. In this situation, the File Binding Component is periodically polling the file system looking for files with a specified filename pattern in a specified directory. When the binding component finds a file that matches its criteria, it reads in the file and starts the JBI Process, which may again cause the input message to be routed between many different binding components and Service Engines. Finally, in this example, the results of the JBI Process are output via a Binding Component. Of course, it is possible that a binding component can act as both a provider and a consumer within the same JBI process. In this case, the file binding component would be initially responsible for reading an input message from the file system. After any JBI processing has occurred, the file binding component would then write out the results of the process to a file. Within the NetBeans Enterprise Pack, the entire set of properties for the file binding component can be edited within the Properties window. The properties for the binding component are displayed when either the input or output messages are selected from the WSDL in a composite application as shown in the following figure.
Read more
  • 0
  • 0
  • 1826

article-image-user-input-validation-tapestry-5
Packt
16 Oct 2009
9 min read
Save for later

User Input Validation in Tapestry 5

Packt
16 Oct 2009
9 min read
Adding Validation to Components The Start page of the web application Celebrity Collector has a login form that expects the user to enter some values into its two fields. But, what if the user didn't enter anything and still clicked on the Log In button? Currently, the application will decide that the credentials are wrong and the user will be redirected to the Registration page, and receive an invitation to register. This logic does make some sense; but, it isn't the best line of action, as the button might have been pressed by mistake. These two fields, User Name and Password, are actually mandatory, and if no value was entered into them, then it should be considered an error. All we need to do for this is to add a required validator to every field, as seen in the following code: <tr> <td> <t:label t_for="userName"> Label for the first text box</t:label>: </td> <td> <input type="text" t_id="userName" t_type="TextField" t:label="User Name" t_validate="required"/> </td></tr><tr> <td> <t:label t_for="password"> The second label</t:label>: </td><td> <input type="text" t_id="password" t_label="Password" t:type="PasswordField" t_validate="required"/></td></tr> Just one additional attribute for each component, and let's see how this works now. Run the application, leave both fields empty and click on the Log In button. Here is what you should see: Both fields, including their labels, are clearly marked now as an error. We even have some kind of graphical marker for the problematic fields. However, one thing is missing—a clear explanation of what exactly went wrong. To display such a message, one more component needs to be added to the page. Modify the page template, as done here: <t:form t_id="loginForm"> <t:errors/> <table> The Errors component is very simple, but one important thing to remember is that it should be placed inside of the Form component, which in turn, surrounds the validated components. Let's run the application again and try to submit an empty form. Now the result should look like this: This kind of feedback doesn't leave any space for doubt, does it? If you see that the error messages are strongly misplaced to the left, it means that an error in the default.css file that comes with Tapestry distribution still hasn't been fixed. To override the faulty style, define it in our application's styles.css file like this: DIV.t-error LI{ margin-left: 20px;} Do not forget to make the stylesheet available to the page. I hope you will agree that the efforts we had to make to get user input validated are close to zero. But let's see what Tapestry has done in response to them: Every form component has a ValidationTracker object associated with it. It is provided automatically, we do not need to care about it. Basically, ValidationTracker is the place where any validation problems, if they happen, are recorded. As soon as we use the t:validate attribute for a component in the form, Tapestry will assign to that component one or more validators, the number and type of them will depend on the value of the t:validate attribute (more about this later). As soon as a validator decides that the value entered associated with the component is not valid, it records an error in the ValidationTracker. Again, this happens automatically. If there are any errors recorded in ValidationTracker, Tapestry will redisplay the form, decorating the fields with erroneous input and their labels appropriately. If there is an Errors component in the form, it will automatically display error messages for all the errors in ValidationTracker. The error messages for standard validators are provided by Tapestry while the name of the component to be mentioned in the message is taken from its label. A lot of very useful functionality comes with the framework and works for us "out of the box", without any configuration or set-up! Tapestry comes with a set of validators that should be sufficient for most needs. Let's have a more detailed look at how to use them. Validators The following validators come with the current distribution of Tapestry 5: Required—checks if the value of the validated component is not null or an empty string. MinLength—checks if the string (the value of the validated component) is not shorter than the specified length. You will see how to pass the length parameter to this validator shortly. MaxLength—same as above, but checks if the string is not too long. Min—ensures that the numeric value of the validated component is not less than the specified value, passed to the validator as a parameter. Max—as above, but ensures that the value does not exceed the specified limit. Regexp—checks if the string value fits the specified pattern. We can use several validators for one component. Let's see how all this works together. First of all, let's add another component to the Registration page template: <tr> <td><t:label t_for="age"/>:</td> <td><input type="text" t_type="textfield" t_id="age"/></td></tr> Also, add the corresponding property to the Registration page class, age, of type double. It could be an int indeed, but I want to show that the Min and Max validators can work with fractional numbers too. Besides, someone might decide to enter their age as 23.4567. This will be weird, but not against the laws. Finally, add an Errors component to the form at the Registration page, so that we can see error messages: <t:form t_id="registrationForm"> <t:errors/> <table> Now we can test all the available validators on one page. Let's specify the validation rules first: Both User Name and Password are required. Also, they should not be shorter than three characters and not longer than eight characters. Age is required, and it should not be less than five (change this number if you've got a prodigy in your family) and not more than 120 (as that would probably be a mistake). Email address is not required, but if entered, should match a common pattern. Here are the changes to the Registration page template that will implement the specified validation rules: <td> <input type="text" t_type="textfield" t_id="userName" t:validate="required,minlength=3,maxlength=8"/></td>...<td> <input type="text" t_type="passwordfield" t_id="password" t:validate="required,minlength=3,maxlength=8"/></td>...<td> <input type="text" t_type="textfield" t_id="age" t:validate="required,min=5,max=120"/></td>...<input type="text" t_type="textfield" t_id="email" t:validate="regexp"/> As you see, it is very easy to pass a parameter to a validator, like min=5 or maxlength=8. But, where do we specify a pattern for the Regexp validator? The answer is, in the message catalog. Let's add the following line to the app.properties file: email-regexp=^([a-zA-Z0-9_.-])+@(([a-zA-Z0-9-])+.)+([a-zA-Z0-9]{2,4})+$ This will serve as a regular expression for all Regexp validators applied to components with ID email throughout the application. Run the application, go to the Registration page and, try to submit the empty form. Here is what you should see: Looks all right, but the message for the age could be more sensible, something like You are too young! You should be at least 5 years old. We'll deal with this later. However for now, enter a very long username, only two characters for password and an age that is more than the upper limit, and see how the messages will change: Again, looks good, except for the message about age. Next, enter some valid values for User Name, Password and Age. Then click on the check box to subscribe to the newsletter. In the text box for email, enter some invalid value and click on Submit. Here is the result: Yes! The validation worked properly, but the error message is absolutely unacceptable. Let's deal with this, but first make sure that any valid email address will pass the validation.   Providing Custom Error Messages We can provide custom messages for validators in the application's (or page's) message catalog. For such messages we use keys that are made of the validated component's ID, the name of validator and the "message" postfix. Here is an example of what we could add to the app.properties file to change error messages for the Min and Max validators of the Age component as well as the message used for the email validation: email-regexp-message=Email address is not valid.age-min-message=You are too young! You should be at least 5 years old.age-max-message=People do not live that long! Still better, instead of hard-coding the required minimal age into the message, we could insert into the message the parameter that was passed to the Min validator (following the rules for java.text.Format), like this: age-min-message=You are too young! You should be at least %s years old. If you run the application now and submit an invalid value for age, the error message will be much better: You might want to make sure that the other error messages have changed too. We can now successfully validate values entered into separate fields, but what if the validity of the input depends on how two or more different values relate to each other? For example, at the Registration page we want two versions of password to be the same, and if they are not, this should be considered as an invalid input and reported appropriately. Before dealing with this problem however, we need to look more thoroughly at different events generated by the Form component.  
Read more
  • 0
  • 0
  • 4197

article-image-managing-content-through-tagging-grails-part-1
Packt
16 Oct 2009
9 min read
Save for later

Managing Content through Tagging in Grails: Part 1

Packt
16 Oct 2009
9 min read
Add basic tagging Tagging is a loose, community-based way of categorizing content. It allows a group of people to categorize by consensus. Anyone is able to tag a piece of content. The more a tag is used, the more meaning it takes on and the more widely used it becomes. This categorization by consensus has been dubbed as folksonomy (http://en.wikipedia.org/wiki/Folksonomy) So let's get started by building our tagging support. Tagging domain model When implementing tagging in our system, we need to consider the following: We must be able to have many tags in our system We must be able to associate a single tag with many different files and messages We need to make sure that new domain objects can be easily tagged without having to change the tagging logic We want to know when a domain object was tagged To satisfy these requirements, we need to create the following new domain classes: Tag—to store the name of the tag. There is one instance of this class per unique tag name in the application. Tagger—to store the relationship from domain objects to a tag. This allows us to store the date a tag was added to a domain object. Let's create these domain classes and then write a test to prove that we can tag a message using this tagging structure. The Tag class We are going to separate the tagging classes out from our application domain classes. Create a folder under grails-app/domain called tagging. This is where we will put the domain model to implement tagging. Our Tag class is extremely simple and holds only a name property: package taggingclass Tag { String name static constrains = { name( blank: false ) } } The Tagger class The next class that we are going to create is the Tagger class. In relational terms, this object represents a link table between a Tag and any other domain class. It is important that the relationship between tagged domain classes and the Tagger relationship class is unidirectional. By this, we mean the domain classes are allowed to know that they can be tagged, but tags do not know which domain classes can be tagged, otherwise every tagged domain class would need a special relationship class. Create the Tagger class as a domain class in the tagging package as follows: package taggingclass Tagger { Tag tag static constraints = { tag( nullable: false ) }} The basics of our tagging model are complete! We now need some logic to allow tags to be created. Create a new service class called TagService under grails-app/services/tagging, as shown below: package taggingclass TagService { boolean transactional = true def createTagRelationships(String spaceDelimitedTags) { return spaceDelimitedTags?.split(' ')?.collect { tagName -> createTagRelationship( tagName ) }}def createTagRelationship(String tagName) {def tag = Tag.findByName(tagName)?: new Tag(name: tagName).save()return new Tagger( tag: tag )} This service provides two utility methods to create new relationships by tag name or by a space delimited string of tag names. The important behavior of these two methods is that they do not allow duplicate tags to be created in the application. If a tag name already exists, the tag will be retrieved from the database and used as the tag in the relationship. Notice that the createTagRelationships method is using the collect method to simplify what would normally take a few more lines of code to achieve. The collect method is dynamically added to any object that can be iterated over. For example, collections, arrays, strings and so on. It takes a closure as its argument and executes this closure for each item in the collection. The return value from each execution of the closure is added to a new collection that the collect method builds up and then returns once it has finished iterating the original collection. In createTagRelationship, we are using another neat language feature of Groovy called the "Elvis operator". It is named so, as it looks like Elvis' hair style. This is a shorter version of the normal Java ternary operator. If the operand being checked is true then the checked operand will be returned as the default, otherwise the alternative operand will be used. So in our example:     def tag = Tag.findByName(tagName) ?: new Tag(name: tagName).save() If a tag can be found from the database then it is used, otherwise a new tag is created. Tagging a message The next step is to allow a message to be tagged. Write some integration tests to make sure the relationships are working before using tagging in the application. In the folder test/integration/app, create the file TaggableIntegrationTests.groovy and add the following code: package appimport tagging.Tagclass TaggableIntegrationTest extends GroovyTestCase { User flancelot protected void setUp() { flancelot = User.findByUsername('flancelot') Tag.list().each { it.delete() } Message.list().each { it.delete() } }} The code above sets up the test data needed to create messages and associate tags to messages. Remember that the flancelot user already exists because it was created by the BootStrap class. The first test will determine that we can add tags to a message and then retrieve messages by tag. Add the following test method to your test class: void testCanRetrieveMessagesByTags() { Message message = new Message(user: flancelot, title: 'tagged', detail: "I've been tagged.").save(flush: true) Message secondMessage = new Message(user: flancelot, title: 'other tagged', detail: "I've been tagged.").save(flush: true) message.addTag('urgent') message.addTag('late') secondMessage.addTag('urgent') def taggedMessages = Message.withTag( 'urgent' ) assertEquals(2, taggedMessages.size()) assertEquals(2, Tag.list().size()) def secondMessages = Message.withTag( 'late' ) assertEquals(1, secondMessages.size()) assertEquals(2, Tag.list().size())} The test above does the following: Creates two new messages Adds the urgent tag to both messages Adds the late tag to one message Checks if we can retrieve both messages by using the urgent tag Checks if only one message is returned for the late tag Notice that the highlighted lines of code have not been implemented yet. To allow this test to pass, we need to add the following methods to the Message domain class: addTag—instance method to allow a message to be tagged withTag—class method to retrieve all messages with a particular tag Add the following method to the Message class (don't forget to import tagging.Tagger): def addTag(String tagName) { tags = (tags)?:[] tags << tagService.createTagRelationship( tagName )} This method simply delegates the creation of the tag relationship off to the TagService class, and then stores the relationship in the tags list. Add the following method to the Message class that retrieves all messages with a given tag name: def static withTag(String tagName) { return Message.withCriteria { tags { tag { eq('name', tagName ) } } }} This method must be static on the Message class, as it is used to load message instances for a given tag. We do not want to have to instantiate a message before we can perform the search. Before running the test, you will notice both of these new methods assume that there is a property on the Message class called tags. This has not yet been created. We need to create a one-to-many relationship from Message to Tagger that will allow messages to be tagged. We also need to inject the TagService into new instances of the Message class so the work for creating a new tag relationship can be delegated. Add the relationship to the Message class and inject TagService as shown below: class Message { def tagService static hasMany = [tags:Tagger] ...} Now we can run our tests by entering the following on the command line: grails test-app We should see some output in the command line similar to: Running test app.TaggableTest... testCanRetrieveMessagesByTags...SUCCESS Tagging a file Now that we have implemented tagging for messages, we need to make tagging available for files. Currently the logic for creating and fetching tags is in the Message domain class. We need to extract this logic so the File domain class can reuse it. It's time to look at how GORM supports inheritance. GORM inheritance The GORM supports inheritance of domain classes by default through the underlying Hibernate framework. Hibernate has a number of strategies for handling inheritance and Grails supports the following two: Table-per-hierarchy—this strategy creates one database table per inheritance hierarchy. This is the default strategy in Grails. Table-per-subclass—this strategy creates a database table for each subclass in an inheritance hierarchy and treats the inheritance (is a) relationship as a foreign key (has a) relationship. Taking our domain as an example, we have two classes. They are Message and File. We are going to make them both extend a super class Taggable, which will handle all of our tagging logic and state. Table-per-hierarchy If we were to choose the table-per-hierarchy strategy, we would end up with one table called Taggable that contained the data for both Message and File. The database structure would look something like: The interesting side-effect of this approach is that all of the fields to be persisted must be nullable. If a File is created and persisted, it is obviously not possible for the fields from Message to be populated. Table-per-subclass By using the table-per-subclass strategy, we would keep two separate tables called Message and File, and both would have the tags relationship inherited from Taggable. So the Message table will look like: We can see in the diagram above that the Message and File tables have remained separate and a table representing the superclass Taggable has been created, which the subclass tables have foreign key relationships to. In the table-per-subclass strategy, a table must exist to represent the inheritance (is a) relationship. We are going to follow the table-per-subclass strategy so that we can retain database level data integrity. The default behavior for GORM is to use the table-per-hierarchy strategy. To override this we must use the mapping property: static mapping = { tablePerHierarchy false}
Read more
  • 0
  • 0
  • 2050
Unlock access to the largest independent learning library in Tech for FREE!
Get unlimited access to 7500+ expert-authored eBooks and video courses covering every tech area you can think of.
Renews at €18.99/month. Cancel anytime
article-image-oracle-vm-management
Packt
16 Oct 2009
6 min read
Save for later

Oracle VM Management

Packt
16 Oct 2009
6 min read
Before we get to manage the VMs in the Oracle VM Manager, let's take a quick look at the Oracle VM Manager by logging into it. Getting started with Oracle VM Manager In this article, we will perform the following actions while exploring the Oracle VM Manager: Registering an account Logging in to Oracle VM Manager Create a Server Pool After we are done with the Oracle VM Manager installation, we will use one of the following links to log on to the Oracle VM Manager: Within the local machine: http://127.0.0.1:8888/OVS Logging in remotely: http://vmmgr:8888/OVS Here, vmmgr refers to the host name or IP address of your Oracle VM Manager host. How to register an account Registering of an account can be done in several ways. If, during the installation of Oracle VM Manager, we have chosen to configure the default admin account "admin", then we can use this account directly to log on to Oracle's IntraCloud portal we call Oracle VM Manager. We will explain later in detail about the user accounts and why we would need separate accounts for separate roles for fine-grained access control; something that is crucial for security purposes. So let's have a quick look at the three available options: Default installation: This option applies if we have performed the default installation ourselves and have gone ahead to create the account ourselves. Here we have the default administrator role. Request for account creation: Contacting the administrator of Oracle VM Manager is another way to attain an account with the privileges, such as administrator, manager, and user. Create yourself: If we need to conduct basic functions of a common user with operator's role such as creating and using virtual machines, or importing resources, we can create a new account ourselves. However, we will need the administrator to assign us the server pools and groups to our account before we can get started. Here by default we are granted a user role. We will talk more about roles later in this article. Now let's go about registering a new account with Oracle VM Manager. Once on the Oracle VM Manager Login page click on the Register link. We are presented with the following screen. We must enter a Username of our choice and a hard-to-crack password twice. Also, we have to fill in our First Name and Last Name and complete the registration with a valid email address. Click Next: Next, we need to confirm our account details by clicking on the Confirm button. Now our account will be created and a confirmation message is displayed on the Oracle VM Manager Login screen. It should be noted that we will need some Server Pools and groups before we can get started. We will have to ask the administrator to assign us access to those pools and groups. It's time now to login to our newly created account. Logging in to Oracle VM Manager Again we will need to either access the URL locally by typing http://127.0.0.1:8888/OVS or by typing the following: http://hostname:8888/ OVS. If we are accessing the Oracle VM Manager Portal remotely, replace the "hostname" with either the FQDN (Fully Qualified Distinguished Name) if the machine is registered in our DNS or just the hostname of the VM Manager machine. We can login to the portal by simply typing in our Username and Password that we just created. Depending on the role and the server pools that we have been assigned, we will be displayed with the tabs upon the screen as shown in the following table. To change the role, we will need to contact our enterprise domain administrator. Only administrators are allowed to change the roles of accounts. If we forget our password, we can click on Forgot Password and on submitting our account name, the password will be sent to the registered email address that we had provided when we registered the account. The following table discusses the assigned tabs that are displayed for each Oracle VM Manager roles:   Role Grants User Virtual Machines, Resources Administrator Virtual Machines, Resources, Servers, Server Pools, Administration Manager Virtual Machines, Resources, Servers, Server Pools   We can obviously change the roles by editing the Profile (on the upper-right section of the portal). As it can be seen in the following screenshot, we have access to the Virtual Machines pane and the Resources pane. We will continue to add Servers to the pool when logged in as admin. Oracle VM management: Managing Server Pool A Server Pool is logically an autonomous region that contains one or more physical servers and the dynamic nature of such pool and pools of pools makes what we call  an infinite Cloud infrastructure. Currently Oracle has its Cloud portal with Amazon but it is very much viable to have an IntraCloud portal or private Cloud where we can run all sorts of Linux and Windows flavors on our Cloud backbone. It eventually rests on the array of SAN, NAS, or other next generation storage substrate on which the VMs reside. We must ensure that we have the following prerequisites properly checked before creating the Virtual Machines on our IntraCloud Oracle VM. Oracle VM Servers: These are available to deploy as Utility Master, Server Master pool, and Virtual Machine Servers. Repositories: Used for Live Migration or Hot Migration of the VMs and for local storage on the Oracle VM Servers. FQDN/IP address of Oracle VM Servers: It is better to have the Oracle VM Servers known as OracleVM01.AVASTU.COM and OracleVM02.AVASTU. COM. This way you don't have to bother about the IP changes or infrastructural relocation of the IntraCloud to another location. Oracle VM Agent passwords: Needed to access the Oracle VM Servers. Let's now go about exploring the designing process of the Oracle VM. Then we will do the following systematically: Creating the Server Pool Editing Server Pool information Search and retrieval within Server Pool Restoring Server Pool Enabling HA Deleting a Server Pool However, we can carry out these actions only as a Manager or an Administrator. But first let's take a look at the decisions on what type of Server Pools will suit us the best and what the architectural considerations could be around building your Oracle VM farm.
Read more
  • 0
  • 0
  • 9730

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

Extending Document Management in Alfresco 3

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

article-image-creating-matrix-report-using-analysis-services-cube
Packt
16 Oct 2009
4 min read
Save for later

Creating a matrix report using the Analysis Services Cube

Packt
16 Oct 2009
4 min read
Reviewing Jayaram's other OLAP related articles may greatly help in understanding this article. Creating the CUBE will be essential to work with this hands-on. If you are already experienced and have an appropriate CUBE to work with you may directly go to the section on deriving a dataset. The CUBE used in this hands-on is a simple one. It does not even have a "Time" dimension although Microsoft in Visual Studio gives a warning that you should have one. However for the purposes of demonstration and doing the hands-on a CUBE without the "Time" dimension should be adequate. The two previous articles on creating a CUBE provide the necessary background for this article. Create a data source with a CUBE Create an Analysis Services Cube (herein called MyNwind.cube) using the TestNorthwind as the database. The Measures and Dimensions for this cube are as shown in the next figure. Start your Report Builder 2.0. Make sure you have started the reporting services using the Reporting Services Configuration Manager. In Report Builder 2.0 click on New | Data source... under Report Data. Provide the following information: Name: SrcCubeSelect connection type: Microsoft SQL Server Analysis ServicesConnection String: Data Source=Hodentek2SANGAM; Initial Catalog=NwindRTM Herein NwindRTM is the name of the Datasource in the Analysis Server. (Alternately you may build this source as shown). Click OK on the Data Source Properties window. Add a dataset to the report Right click on SrcCube and choose Add DataSet... In the Dataset Properties window make the following changes: Name: QryCubeData source: SrcCubeQuery Type: text Click on Query Designer... button. At first you will see only the Metadata of the cube displayed as in the left side of the next figure. Expand Customers, Orders, and Products in the dimensions and drop them on the list header in the bottom pane on the right hand side of the next figure. Drag and drop Freight from Orders under Measures as shown. For each of the dimensions using an operator create a filter expression as shown. For example, the Products included in the query range from Product IDs 3 to 9; the Customer ID (some specific ones are chosen), similarly theCategory ID (only those with ID's 2, 3, 4, and 5 are included in the query). The design of the query with these filtering choices is as shown in the previous figure. The filtering tool is very flexible and all the items namely Dimension, Hierarchy, Operator and Filter Expression can be designed in this interface using either drop-down pick lists choices or drop-downs with checkboxes as shown here. Run the query by clicking on the toolbar item (!) and review the results. Click OK on the Query Designer. The Dataset Properties window shows up with this query designed according to your choices in the query item window as shown. Close the Dataset Properties window. Report Design In Report Builder 2.0 click on New Table or Matrix wizard. In the Choose a dataset window accept the default and click Next. Drag and drop Customer_ID in the Row Groups drop area. Drag and drop Order_ID below Customer_ID in the Row Groups drop area. Drag and drop Product_ID in the Column Groups drop area. Drag and drop Freight into the Values drop area and click on Next. In the Choose the layout window accept Expand/collapse groups and choose the option Stepped, subtotal above. Click Next and choose some style (herein Ocean) and click Finish. The final report design is as shown here. Click Home | Run. The report gets displayed after processing as shown here. The orders from a particular customer have been expended in this view. Summary Report Builder 2.0 can used to author reports based on Analysis Services Cubes. The interface is very flexible and the Query Designer is very easy to use as shown in this article. If you have read this article you may be interested to view : Creating an Analysis Services Cube with Visual Studio 2008 - Part 1 Creating an Analysis Services Cube with Visual Studio 2008 - Part 2
Read more
  • 0
  • 0
  • 3698

article-image-arrays-and-control-structures-object-oriented-javascript
Packt
16 Oct 2009
4 min read
Save for later

Arrays and Control Structures in Object-Oriented JavaScript

Packt
16 Oct 2009
4 min read
Arrays Now that you know the basic primitive data types in JavaScript, it's time to move to a more interesting data structure—the array. To declare a variable that contains an empty array, you use square brackets with nothing between them: >>> var a = [];>>> typeof a;"object" typeof returns "object", but don't worry about this for the time being, we'll get to that when we take a closer look at objects. To define an array that has three elements, you do this: >>> var a = [1,2,3]; When you simply type the name of the array in the Firebug console, it prints the contents of the array: >>> a[1, 2, 3] So what is an array exactly? It's simply a list of values. Instead of using one variable to store one value, you can use one array variable to store any number of values as elements of the array. Now the question is how to access each of these stored values? The elements contained in an array are indexed with consecutive numbers starting from zero. The first element has index (or position) 0, the second has index 1 and so on. Here's the three-element array from the previous example: Index Value 0 1 1 2 2 3 In order to access an array element, you specify the index of that element inside square brackets. So a[0] gives you the first element of the array a, a[1] gives you the second, and so on. >>> a[0]1>>> a[1]2 Adding/Updating Array Elements Using the index, you can also update elements of the array. The next example updates the third element (index 2) and prints the contents of the new array. >>> a[2] = 'three';"three">>> a[1, 2, "three"] You can add more elements, by addressing an index that didn't exist before. >>> a[3] = 'four';"four">>> a[1, 2, "three", "four"] If you add a new element, but leave a gap in the array, those elements in between are all assigned the undefined value. Check out this example: >>> var a = [1,2,3];>>> a[6] = 'new';"new">>> a[1, 2, 3, undefined, undefined, undefined, "new"] Deleting Elements In order to delete an element, you can use the delete operator. It doesn't actually remove the element, but sets its value to undefined. After the deletion, the length of the array does not change. >>> var a = [1, 2, 3];>>> delete a[1];true>>> a[1, undefined, 3] Arrays of arrays An array can contain any type of values, including other arrays. >>> var a = [1, "two", false, null, undefined];>>> a[1, "two", false, null, undefined]>>> a[5] = [1,2,3][1, 2, 3]>>> a[1, "two", false, null, undefined, [1, 2, 3]] Let's see an example where you have an array of two elements, each of them being an array. >>> var a = [[1,2,3],[4,5,6]];>>> a[[1, 2, 3], [4, 5, 6]] The first element of the array is a[0] and it is an array itself. >>> a[0][1, 2, 3] To access an element in the nested array, you refer to the element index in another set of square brackets. >>> a[0][0]1>>> a[1][2]6 Note also that you can use the array notation to access individual characters inside a string. >>> var s = 'one';>>> s[0]"o">>> s[1]"n">>> s[2]"e" There are more ways to have fun with arrays, but let's stop here for now, remembering that: An array is a data store An array contains indexed elements Indexes start from zero and increment by one for each element in the array To access array elements we use the index in square brackets An array can contain any type of data, including other arrays
Read more
  • 0
  • 0
  • 5531
article-image-getting-started-scratch-14-part-2
Packt
16 Oct 2009
7 min read
Save for later

Getting Started with Scratch 1.4 (Part 2)

Packt
16 Oct 2009
7 min read
Add sprites to the stage In the first part we learned that if we want something done in Scratch, we tell a sprite by using blocks in the scripts area. A single sprite can't be responsible for carrying out all our actions, which means we'll often need to add sprites to accomplish our goals. We can add sprites to the stage in one of the following four ways: paint new sprite, choose new sprite from file, get a surprise sprite, or by duplicating a sprite. Duplicating a sprite is not in the scope of this article. The buttons to insert a new sprite using the other three methods are directly above the sprites list. Let's be surprised. Click on get surprise sprite (the button with the "?" on it.). If the second sprite covers up the first sprite, grab one of them with your mouse and drag it around the screen to reposition it. If you don't like the sprite that popped up, delete it by selecting the scissors from the tool bar and clicking on the sprite. Then click on get surprise sprite again. Each sprite has a name that displays beneath the icon. See the previous screenshot for an example. Right now, our sprites are cleverly named Sprite1 and Sprite2. Get new sprites The create new sprite option allows you to draw a sprite using the Paint Editor when you need a sprite that you can't find anywhere else. You can also create sprites using third-party graphics programs, such as Adobe Photoshop, GIMP, and Tux Paint. If you create a sprite in a different program, then you need to import the sprite using the choose new sprite from file option. Scratch also bundles many sprites with the installation, and the choose new sprite from file option will allow you to select one of the included files. The bundled sprites are categorized into Animals, Fantasy, Letters, People, Things, and Transportation, as seen in the following screenshot: If you look at the screenshot carefully, you'll notice the folder path lists Costumes, not sprites. A costume is really a sprite. If you want to be surprised, then use the get surprise sprite option to add a sprite to the project. This option picks a random entry from the gallery of bundled sprites. We can also add a new sprite by duplicating a sprite that's already in the project by right-clicking on the sprite in the sprites list and choosing duplicate (command C on Mac). As the name implies, this creates a clone of the sprite. The method we use to add a new sprite depends on what we are trying to do and what we need for our project. Time for action – spin sprite spin Let's get our sprites spinning. To start, click on Sprite1 from the sprites list. This will let us edit the script for Sprite1. From the Motion palette, drag the turn clockwise 15 degrees block into the script for Sprite1 and snap it in place after the if on edge, bounce block. Change the value on the turn block to 5. From the sprites list, click on Sprite2. From the Motion palette, drag the turn clockwise 15 degrees block into the scripts area. Find the repeat 10 block from the Control palette and snap it around the turn clockwise 15 degrees block. Wrap the script in the forever block. Place the when space key pressed block on top of the entire stack of blocks. From the Looks palette, snap the say hello for 2 secs block onto the bottom of the repeat block and above the forever block. Change the value on the repeat block to 100. Change the value on the turn clockwise 15 degrees block to 270. Change the value on the say block to I'm getting dizzy! Press the Space bar and watch the second sprite spin. Click the flag and set the second sprite on a trip around the stage. What just happened? We have two sprites on the screen acting independently of each other. It seems simple enough, but let's step through our script. Our cat got bored bouncing in a straight line across the stage, so we introduced some rotation. Now as the cat walked, it turned five degrees each time the blocks in the forever loop ran. This caused the cat to walk in an arc. As the cat bounced off the stage, it got a new trajectory. We told Sprite2 to turn 270 degrees for 100 consecutive times. Then the sprite stopped for two seconds and displayed a message, "I'm getting dizzy!" Because the script was wrapped in a forever block, Sprite2 started tumbling again. We used the space bar as the control to set Sprite2 in motion. However, you noticed that Sprite1 did not start until we clicked the flag. That's because we programmed Sprite1 to start when the flag was clicked. Have a go hero Make Sprite2 less spastic. Instead of turning 270 degrees, try a smaller value, such as 5. Sometimes we need inspiration So far, we've had a cursory introduction to Scratch, and we've created a few animations to illustrate some basic concepts. However, now is a good time to pause and talk about inspiration. Sometimes we learn by examining the work of other people and adapting that work to create something new that leads to creative solutions. When we want to see what other people are doing with Scratch, we have two places to turn. First, our Scratch installation contains dozens of sample projects. Second, the Scratch web site at http://scratch.mit.edu maintains a thriving community of Scratchers. Browse Scratch's projects Scratch includes several categories of projects for Animation, Games, Greetings, Interactive Art, Lists, Music and Dance, Names, Simulations, Speak up, and Stories. Time for action – spinner Let's dive right in. From the Scratch interface, click the Open button to display the Open Project dialog box, as seen in the following screenshot. Click on the Examples button. Select Simulations and click OK. Select Spinner and click OK to load the Spinner project. Follow the instructions on the screen and spin the arrow by clicking on the arrow. We're going to edit the spinner wheel. From the sprites list, click on Stage. From the scripts area, click the Backgrounds tab. Click Edit on background number 1 to open the Paint Editor. Select a unique color from the color palette, such as purple. Click on the paint bucket from the toolbar, then click on one of the triangles in the circle to change its color. The paint bucket is highlighted in the following screenshot. Click OK to return to our project. What just happened? We opened a community project called Spinner that came bundled with Scratch. When we clicked on the arrow, it spun and randomly selected a color from the wheel. We got our first look at a project that uses a background for the stage and modified the background using Scratch's built-in image editor. The Paint Editor in Scratch provides a basic but functional image editing environment. Using the Paint Editor, we can create a new sprite/background and modify a sprite/background. This can be useful if we are working with a sprite or background that someone else has created. Costume versus background A costume defines the look of a sprite while a background defines the look of the stage. A sprite may have multiple costumes just as the stage can have multiple backgrounds. When we want to work with the backgrounds on the stage, we use the switch to background and next background blocks. We use the switch to costume and next costume blocks when we want to manipulate a sprite's costume. Actually, if you look closely at the available looks blocks when you're working with a sprite, you'll realize that you can't select the backgrounds. Likewise, if you're working with the stage, you can't select costumes.
Read more
  • 0
  • 0
  • 5462

article-image-optimizing-lighttpd
Packt
16 Oct 2009
5 min read
Save for later

Optimizing Lighttpd

Packt
16 Oct 2009
5 min read
If our Lighttpd runs on a multi-processor machine, it can take advantage of that by spawning multiple versions of itself. Also, most Lighttpd installations will not have a machine to themselves; therefore, we should not only measure the speed but also its resource usage. Optimizing Compilers: gcc with the usual settings (-O2) already does quite a good job of creating a fast Lighttpd executable. However, -O3 may nudge the speed up a tiny little bit (or slow it down, depending on our system) at the cost of a bigger executable system. If there are optimizing compilers for our platform (for example, Intel and Sun Microsystems each have compilers that optimize for their CPUs), they might even give another tiny speed boost. If we do not want to invest money in commercial compilers, but maximize on what gcc has to offer, we can use Acovea, which is an open source project that employs genetic algorithms and trial-and-error to find the best individual settings for gcc on our platform. Get it from http://www.coyotegulch.com/products/acovea/ Finally, optimization should stop where security (or, to a lesser extent, maintainability) is compromised. A slower web server that does what we want is way better than a fast web server obeying the commands of a script kiddie. Before we optimize away blindly, we better have a way to measure the "speed". A useful measure most administrators will agree with is "served requests per second". http_load is a tool to measure the requests per second. We can get it from http://www.acme.com/software/http_load/. http_load is very simple. Give it a site to request, and it will flood the site with requests, measuring how many are served in a given amount of time. This allows a very simplistic approach to optimizing Lighttpd: Tweak some settings, run http_load with a sufficient realistic scenario, and see if our Lighttpd handles more or less requests than before. We do not yet know where to spend time optimizing. For this, we need to make use of timing log instrumentation that has been included with Lighttpd 1.5.0 or even use a profiler to see where the most time is spent. However, there are some "big knobs" to turn that can increase performance, where http_load will help us find a good setting. Installing http_load http_load can be downloaded as a source .tar file (which was named .tar.gz for me, though it is not gzipped). The version as of this writing is 12Mar2006. Unpack it to /usr/src (or another path by changing the /usr/src) with: $ cd /usr/src && tar xf /path/to/http_load-12Mar2006.tar.gz$ cd http_load-12Mar2006 We can optionally add SSL support. We may skip this if we do not need it. To add SSL support we need to find out where the SSL libs and includes are. I assume they are in /usr/lib and /usr/include, respectively, but they may or may not be the same on your system. Additionally, there is a "SSL tree" directory that is usually in /usr/ssl or /usr/local/ssl and contains certificates, revocation lists, and so on. Open the Makefile with a text editor and look at line 11 to 14, which reads: #SSL_TREE = /usr/local/ssl#SSL_DEFS = -DUSE_SSL#SSL_INC = -I$(SSL_TREE)/include#SSL_LIBS = -L$(SSL_TREE)/lib -lssl -lcrypto Change them to the following (assuming the given directories are correct): SSL_TREE = /usr/sslSSL_DEFS = -DUSE_SSLSSL_INC = -I/usr/includeSSL_LIBS = -L/usr/lib -lssl -lcrypto Now compile and install http_loadwith the following command: $ make all install Now we're all set to load-test our Lighttpd. Running http_load tests We just need a URL file, which contains URLs that lead to the pages our Lighttpd serves. http_load will then fetch these pages at random as long as, or as often as we ask it to. For example, we may have a front page with links to different articles. We can just start putting a link to our front page into the URL file, which we will name urls to get started; for example, http://localhost/index.html. Note that the file just contains URLs, nothing less, nothing more (for example, http_load does not support blank lines). Now we can make our first test run: $ http_load -parallel 10 -seconds 60 urls This will run for one minute and try to open 10 connections per second. Let's see if our Lighttpd keeps up: 343 fetches, 10 max parallel, 26814 bytes, in 60 seconds78.1749 mean bytes/connection5.71667 fetches/sec, 446.9 bytes/secmsecs/connect: 290.847 mean, 9094 max,15 minmsecs/first-response: 181.902 mean, 9016 max, 15 minHTTP response codes: code 200 - 327   As we can see, it does. http_load needs one of the two start conditions and one of the two stop conditions plus a URL file to run. We can create the URL file manually or crawl our document root(s) with the following python script called crawl.py: #!/usr/bin/python#run from document root, pipe into URLs file. For example:# /path/to/docroot$ crawl.py > urlsimport os, re, syshostname = "http://localhost/"for (root, dirs, files) in os.walk("."): for name in files: filepath = os.path.join(root, name) print re.sub("./", hostname, filepath)   You can download the crawl.oy file from http://www.packtpub.com/files/code/2103_Code.zip. Capture the output into a file to use as URL file. For example, start the script from within our document root with: $ python crawl.py > urls This will give us a urls file, which will make http_load try to get all files (given that we have specified enough requests). Then we can start http_load as discussed in the preceding example. http_load takes the following options:  
Read more
  • 0
  • 0
  • 7278

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

Views, URLs, and Generic Views in Django 1.0

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

Competitive Service and Contract Management in SAP Business ONE Implementation: Part 2

Packt
16 Oct 2009
8 min read
In the first half of this 2-part article series, we looked at the service module so as to evaluate potential actions that are triggered, based on service-related information. We also introduced a concept which explained how to utilize the service module features to establish a guaranteed response time for customers. We also learnt about : Key terms - The common terminology related to service management were covered. Although nothing major , we went about learning what the terms entail with regards to the SAP system. Service module core functions – In this section, the available functions and features were put into perspective—what is available and how much we can expect from it.  You also learnt what service operations mean. Case study and your own project – The available features of the service module were implemented for the case study. By doing so, knowledge was provided to implement the service module in your own business. We also reviewed some guidelines which enabled you to translate the case study implementation into a set of activities for your own project. Let's begin with Service reports. Service reports The crucial element of each module is the information that can easily be extracted for reporting purposes. SAP provides a series of canned reports for the service module. The Service Calls report provides information about service call activities based on the selected criteria. You can filter this report by timeframe of service call creation and also by resolution time. Additional filter ranges are available for Customer Code, Handled By, Item, and also Queue ID. In addition, the report allows filtering by Problem Type, Priority, Call Type, Origin, Call Status, and Overdue Calls. You can see that there is a wide range of options to obtain information. It is important to note that reporting can utilize the information only if all of the data is properly collected using the SAP forms. In case no options are selected for filtering, the report defaults to select all of the available information. The following additional service reports are available and have almost identical filtering capabilities as the service calls report: Service Calls by Queue, Response Time by Assigned to, Average Closure Time. The Service Contracts report(seen below) helps you manage the status of all maintenance contracts. If you've ever had to manage maintenance contracts with customers, you will appreciate the ease of obtaining the information here. You can filter this report by Customer Code, Start Date Range, End Date Range and Termination Date Range. In addition, this report can be further filtered by Contract Type, Contract Status, and Service Type. The customer equipment card report allows information to be obtained about items sold to customers based on serial number tracking. Each serial number has its own contract with expiration which is usually based on the purchase date. This report is able to be filtered by customer and item code. In addition, a more global filter can be used such as item group. The Service Monitor report provides a more real-time view of the service pipeline. We already covered this report in the previous section. Finally, My Reports includes My Service Calls, My Open Service Calls, and My Overdue Service Calls. Those reports conveniently filter the information based on the current login name. Therefore, if you run the report, you will only see information that is relevant for you based on the login. Limitations I have already mentioned that you can look at SAP Business ONE as the operating system for your business. You can use industry add-ons to seamlessly transform the standard features into an industry solution that is specific to your requirements. Therefore, let's evaluate some add-ons I've worked with that are related to the service module—specifically, the Enprise Job Costing module and the solution from Navigator called ServiceONE. By looking at these add-ons, we can also learn the limitations of the standard service module. For example, since the Navigator promises to have all of the information available in one view, we realize that in SAP, we sometimes need to jump to different forms to get where we wanted to be initially. Let's further evaluate the features of these add-ons. The Enprise Job Costing solution introduces a web-based timesheet. This is an obvious feature that is not directly available in the SAP standard configuration. First, I will look at the Navigator solution and will then follow the Enprise offering. Often, there is more than one add-on providing industry-specific features. You then need to evaluate both solutions and decide which one best fi ts your requirements. Please note that I am presenting the add-on features to better define the limitations of the SAP Standard Business ONE features. Job Costing add-on by Enprise The Enprise Job Costing add-on is one of the first industry-specific solutions that gained widespread adoption as a standard for companies that required a detailed job costing solution. The advantage of this solution is that it is based on true expertise in the job costing area as it relates to the SAP service module. Let's look at a scenario that is very common for companies that work in the service industry and require what is known as job costing. However, I would first like to take the opportunity to explain job costing a bit. Job costing allows the profit and loss for specific services provided to be calculated. For example, if you have a company that sends out technicians to customer sites for performing equipment repairs, you need to make sure that the invoiced amount exceeds the cost you incur. The following workflow may be common in this environment: Serialized items are delivered to customers with each having a warranty contract that may or may not include services, parts, and replacements. Services may be performed on serialized items delivered by you or by another company. A service call may lead to a proposal (job) which will then be ordered. Technicians may use a timesheet to report the status and time. Timesheet entries must be possible via a mobile device or the Web. Furthermore, time entered needs to be approved before it is relevant for invoicing. A job may lead to subjobs that require unique management of related costs. As the number of jobs increase, you will require "work in progress" reporting. Estimating a job is crucial. Therefore, technicians need to be able to create estimates. It must then be possible to translate those estimates into orders and contracts. As services may require replacement parts, a feature is required that allows optimized picking of relevant items for a specific job. Complex jobs require milestone payments. This needs to be implemented in the contracts. The invoicing system needs to be integrated with the way services are completed. For example, milestone payments, fixed priced billing, and partial invoicing are common requirements in the service industry. The Enprise Job Costing add-on resides in its own menu item called Job Costing. As you can see below, the menu items are well defined and provide a quick overview of the available features. In addition, it is important to note that the features seamlessly appear within the SAP interface: By selecting the Job Entry form, the powerful features come to light. As you can see in the following screenshot, the form allows searching jobs based on Status,Type, Properties, Category, and Entered By. The resulting list is shown in the lower pane. We can use this interface to search for specific jobs, and then click on the Bulk Invoice button as highlighted in the screenshot. This automates the invoicing process based on a clear, uncluttered form. Please note that we do not need to jump between multiple forms. The Direct Time Entry form(seen below) is basically a timesheet. Therefore, technicians can use this to enter the time they spend on projects. Please note the buttons in the lower right that allow importing from the Web and also from an Excel clipboard. Enprise provides a web-based timesheet from which we can import data. However, it is important to note that we can also import from an Excel clipboard. This way, we can use the date that technicians entered in their laptops. The contract list provides a link where the Enprise-enhanced contract management surfaces: The contract management allows milestones for a contract. Each milestone to be established could lead to a milestone payment. In addition, we can directly jump to the related invoices by using the Show Invoices button: Enprise has adopted the concept of master data. For this purpose, the Job Master Data form was established. This is consistent with the SAP concept. Each job is defined and configured with specific parameters, which later drive the transactions that are based on this master data. For example, we can define the job parameters alongside a list of subjobs. In addition, documents can be attached as attachments: Advanced service functionality using ServiceONE by Navigator Navigator provides a wide range of valuable add-ons. Each add-on is valuable. However, the key advantage of Navigator is the comprehensive portfolio of add-ons the cover almost all aspects of SAP Business ONE. In particular, the fact that Navigator also provides a mobile solution, which connects handheld computers with SAP Business ONE, extends the reach of the available functionality beyond the boundaries of the SAP client interface. Therefore, a mobile field service does not need to use a web-based timesheet, but could directly interact using mobile devices. However, you may need to purchase another add-on to accomplish this.
Read more
  • 0
  • 0
  • 2641

Packt
16 Oct 2009
6 min read
Save for later

Network Configuration—IPv6 with FreeBSD

Packt
16 Oct 2009
6 min read
Several methods were introduced to reduce the usage of IP addresses in the internet including: Classless Interdomain Routing (CIDR): This introduced the death of classful addressing (for example Class A, B, C) by a new subnetting method which is not limited, unlike the classful method. Network Address Translation (NAT): Using NAT you do not need to use public IP addresses on your internal hosts. Using CIDR subnets and NAT only helped IPv4 to live a few years longer, but was not the ultimate cure to the problem. Besides the addressing issues, there were other problems with IPv4 which could not be easily solved. These issues include the following: The size of internet routing tables was growing rapidly and this forced backbone providers to upgrade their networking gears. The IPv4 was very inefficient for high throughput links and did not support QoS by nature. Back in the early 90s, IETF had started a workgroup to solve the deficiencies of the IP protocol. In 1995, the IETF published the initial drafts of IPv6 as the next generation IP. Since then, the protocol has matured enormously and been implemented in many operating systems. IPv6 Facts If you are not familiar with IPv6, here is a very quick look at the difference between IPv4 and IPv6. (For a more detailed insight into IPv6 and its configuration in various operating systems, it is recommended that you read Running IPv6 book by Iljitsch van Beijnum). Fact One—Addressing Addressing in IPv6 is quite different from legacy IPv4 addresses. IPv6 uses 128-bit address space unlike the 32-bit addressing system in IPv4. A typical IPv6 address would look like—2002:a00:1:5353:20a:95ff:fef5:246e Fact Two—Address Types There are 4 types of addresses in IPv6: Unicast: A typical IPv6 address you use on a host. Multicast: Addresses that start with ff:: are equivalent to IPv4 multicast. Anycast: A typical IPv6 address that is used on a router. Reserved: Includes loopback, link-local, site-local, and so on. Fact Three—ARP There is no ARP! MAC to IP mapping is no longer needed as MAC addresses are embedded into IPv6 addresses. Instead, ND is born. ND is used to auto-configure addresses on hosts, duplicated detection, and so on. Fact Four—Interface Configuration If you are new to IPv6, you will be shocked to see an IPv6 address, telling yourself that you are in trouble assigning addresses to interfaces or remembering the addresses. However, it is not all that hard. In most cases, you can have your host autoconfigure IPv6 address on its interfaces. Typically, you should set this up only on your network gateway (router) manually. Using IPv6 Running FreeBSD 7, the kernel is already IPv6 enabled. However, you should manually enable IPv6 in the UserLand, by adding the following line to the /etc/rc.conf configuration file: ipv6_enable="YES" And manually start the appropriate rc script (or reboot the system) for the changes to take effect: # /etc/rc.d/network_ipv6 start This will enable IPv6 on all interfaces that are IPv6 capable. This behavior is changed by modifying the following variable in the /etc/rc.conf file: ipv6_network_interfaces="fxp0 bge0" This will enable IPv6 support on specified interfaces. The default value for this variable is auto. Once you enable IPv6, interfaces will discover the IPv6 enabled routers on the network and build their own IPv6 addresses based on the network prefix they receive from the router. Configuring Interfaces In a typical scenario, IPv6 network stack will automatically look for an IPv6 enabled router on the same network for each interface and try to automatically configure the IPv6 address on the interface. The following is an example of an automatically configured interface(replace the $ with %): # ifconfig ed0 ed0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500 ether 00:1c:42:8d:5d:bf inet6 fe80::21c:42ff:fe8d:5dbf$ed0 prefixlen 64 scopeid 0x1 inet 192.168.0.225 netmask 0xffffff00 broadcast 192.168.0.255 inet6 2a01:3c8::21c:42ff:fe8d:5dbf prefixlen 64 autoconf media: Ethernet autoselect (10baseT/UTP) Beside the IPv4 address, there are two IPv6 addresses on the interface. One address begins with fe80:: and identified with the scopeid 0x1 tag, which is called a link-local address. Another address begins with 2a01:3c8::, which is the unicast address of this interface. The unicast address prefix is obtained from the IPv6 router on the network. The whole address is created using the 64 bits Extended Unique Identifier (EUI-64) algorithm, which consists of the hosts MAC address with some minor modifications. The link-local address (that is from the reserved address pool) always starts with fe80:: and is used for local network usage. This can be compared with RFC1819 private addresses that are suitable for local use. The network stack will automatically assign a link-local address to each IPv6 enabled interface, regardless whether an IPv6 router is discovered on the network. This means that in a scenario of a home network or a lab network, you do not need to run an IPv6 router or have a valid IPv6 prefix in order to establish an IPv6 network. All the hosts will be automatically provisioned with a link-local address, so they can exchange IPv6 traffic. The network discovery protocol (NDP) helps the host find the router on the network and then create a unicast address for the interface. NDP is known as the equivalent to ARP protocol in IPv6. The ndp(8) utility is used to control the behavior of this protocol: # ndp -a Neighbor Linklayer Address Netif Expire S Flags 2a01:3c8:: 0:16:cb:98:d4:bf ed0 20s R R 2a01:3c8::21c:42ff:fe8d:5dbf 0:1c:42:8d:5d:bf ed0 permanent R fe80::216:cbff:fe98:d4bf$ed0 0:16:cb:98:d4:bf ed0 23h58m48s S R fe80::21c:42ff:fe8d:5dbf$ed0 0:1c:42:8d:5d:bf ed0 permanent R fe80::1%lo0 (incomplete) lo0 permanent R The above example shows the discovered IPv6 hosts(replace the $ with %). The ed0 interface is connected to an IPv6 enabled network and receives a valid prefix via a router (the first entry of the list). The second entry is the unicast address of the ed0. The third and the fourth entries are link-local address for the router and our host. And the last entry belongs to the local host. As you have seen so far, there are some special (reserved) IPv6 addresses. The following table shows a list of reserved addresses:   Address Name Description :: Unspecified Equivalent to 0.0.0.0 in Pv4 ::1 Loopback address Equivalent to 127.0.0.1 in IPv4 fe80:: Link-local fec0:: Site-local ff00:: Multicast   In case you want to configure the static IPv6 address on an interface, it can be done as in a typical IPv4 scenario: # ifconfig vr0 inet6 2a01:3c8::21c:42ff:dead:beef prefixlen 64 This will manually configure an IP address on the specified interface. Note the prefixlen keyword that is equivalent to subnet mask in IPv4.
Read more
  • 0
  • 0
  • 4973
Modal Close icon
Modal Close icon