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-building-queries-visually-mysql-query-browser
Packt
23 Oct 2009
3 min read
Save for later

Building Queries Visually in MySQL Query Browser

Packt
23 Oct 2009
3 min read
MySQL Query Browser, one of the open source MySQL GUI tools from MySQL AB, is used for building MySQL database queries visually. In MySQL Query Browser, you build database queries using just your mouse—click, drag and drop! MySQL Query Browser has plenty of visual query building functions and features. This article shows two examples, building Join and Master-detail queries. These examples will demonstrate some of these functions and features. Join Query A pop-up query toolbar will appear when you drag a table or column from the Object Browser’s Schemata tab to the Query Area. You drop the table or column on the pop-up query toolbar’s button to build your query. The following example demonstrates the use of the pop-up query toolbar to build a join query that involves three tables and two types of join (equi and left outer). Drag and drop the product table from the Schemata to Add Table(s) button. A SELECT query on the product table is written in the Query Area. Drag and drop the item table from Schemata to the JOIN Table(s) button on the Pop-up Query Toolbar. The two tables are joined on the foreign-key, product_code. If no foreign-key relationship exists, the drag and drop won’t have any effect. Drag and drop the order table from Schemata to the LEFT OUTER JOIN button on the Pop-up Query Toolbar. Maximize query area by pressing F11. You get a larger query area, and your lines are sequentially numbered (for easier identification). Move the FROM clause to its next line, by putting your cursor just before the FROM word and press Enter. Similarly, move the ON clause to its next line. Now, you can see all lines completely, and that the item table is left join to the order table on their foreign-key relationship column, the order_number column. As of now our query is SELECT *, i.e. selecting all columns from all tables. Let’s now select the columns we’d like to show at the query’s output. For example, drag and drop the order_number from the item table, product_name from the product table, and then quantity from the item table. (If necessary, expand the table folders to see their columns). The sequence of the selecting the columns is reflected in the SELECT clause (from left to right). Note that you can’t select column from the left join of the order table (if you try, nothing will happen) Next, add an additional condition. Drag and drop the amount column on the WHERE button in the Pop-up Query Toolbar. The column is added, with an AND, in the WHERE clause of the query. Type in its condition value, for example, > 1000. To finalize our query, drag and drop product_name on the ORDER button, and then, order_number (from item table, not order table) on the GROUP button. You’ll see that the GROUP BY and ORDER clauses are ordered correctly, i.e. the GROUP BY clause first before the ORDER BY, regardless of your drag & drop sequence. To test your query, click the Execute button. Your query should run without any error, and display its output in the query area (below the query).  
Read more
  • 0
  • 0
  • 4920

article-image-blender-3d-interview-allan-brito
Packt
23 Oct 2009
8 min read
Save for later

Blender 3D: Interview with Allan Brito

Packt
23 Oct 2009
8 min read
Meeba Abraham: Hi Allan, thank you for talking to us today, why don’t you tell us a bit about yourself and your background; how did you start working with Blender? Allan Brito: Hi, and thanks for this opportunity to talk a bit about myself. Well, I’m a 29 year-old architect from Brazil. After my graduation, I started working on visualization projects, mostly on 3ds Max for a small studio here in Brazil. After two years I started teaching 3D modeling and animation and I fell in love with teaching. I still teach 3D animation and modeling at a College here. With the help of my teaching experience, I began writing manuals and tutorials about 3D animation. Eventually, I decided to write a book about Blender in Portuguese, and the book was a huge success in Brazil. Currently I`m working on the third edition of this book. With the book, I also needed a way to keep in touch with the readers and discuss about Blender and 3D related stuff. So I started a web site (www.allanbrito.com), where I regularly write short articles and tutorials about Blender and its comparison with other 3D packages. Today the web site has grown considerably, and I continue to update it with content on Blender and other 3D software tools. Meeba Abraham: How long have you been working with it? Allan Brito: My first contact with Blender 3D was in 2003. I was invited by a friend to check out a great open source software for 3D visualization. I was really impressed by Blender, its potential, and the lightweight of the software. Coming from a 3ds Max background, it was a bit hard to get used to the interface and the keyboard shortcuts, but after a few weeks I started getting used to it. After the learning process, I started to use Blender as the main tool for my projects. I can`t say that it was easy to use at first, but with time Blender simply grew on me and became my main tool for my projects. Meeba Abraham: Can you tell about some of the key features of Blender that makes it a viable option to other professional 3D software? Allan Brito: There are many features in Blender that other professional 3D suites do not have. For instance, the integrated Game Engine, which allows you to produce interactive animations, is just awesome! For 3D modeling, Blender has a sculpt module where artists can create 3D models only by sculpt geometry in a way similar to what sculpting tools such as ZBrush and MudBox provides. The node editor in Blender is also an incredible tool to create materials and for post-production. Post-production is a powerful tool in Blender. There is a sequencer editor that works like a video editor. You can cut, join, and post-process videos in the sequence editor. For instance, an animator can create a full animation without the need of any other software. Recently, the Big Buck Bunny project introduced some great tools for character animation in Blender, like better fur, a new and improved particle system, new and improved UV Mapping and much more. I strongly recommend a visit to www.blender.org to check out the full list of features, which is huge. Meeba Abraham: Why is Blender an important 3D application that an aspiring graphics artist should consider using? Allan Brito: I believe that Blender has a great set of features that can help a graphic artist create some impressive art work. Why Blender? I guess the best answer is; why not? All the features offered by other 3D animation software are also available in Blender, such as character animation, physics simulation, particle animation, and much more. And with Blender being a free software, you won’t have to get a single license and be bounded to only one workstation. Besides the features, I believe in the community nature of Blender. If you feel a tool or feature is missing, just make a suggestion to the community or make the feature yourself! Meeba Abraham: Over the years, Blender has grown in popularity. What, in your opinion, are some of the main reasons for this? Allan Brito: In the last few years Blender gained many features that only the so-called high-end and expansive 3D software had. This puts the spotlight right into Blender, and some old and experienced professionals are using Blender today, to take a look at these advanced features, and they like it. Besides the features, the Blender Foundation is doing a great job by supporting Blender and promoting it outside the community. They organize conferences and projects to show the potentials of Blender as a 3D animation package. The last open movie—Big Buck Bunny—supported by the community is a great example of that. Meeba Abraham: Since Blender is an open source 3D application, the Blender community plays an important role in its growth. Can you shed some light on the blender community? How have they helped to popularize Blender? Allan Brito: What can I say? The Blender community is great and has been supporting the development of Blender for a long time. The last open movie is a great example of what this community can do. Big Buck Bunny is a project mainly created by the Blender community. Artists could buy the DVD of the animation even before the project started. And when the animation was finished, all Blender users could buy a shiny DVD of the animation that contains tutorials and all source files of the animation. Now, what if Pixar gave away all the production files of their animations. And even of you don’t want to buy the DVD, you can still download all of the content for free from the project Web site, www.bigbuckbunny.org. This is a great example of the Blender community spirit and how much support Blender gets from around the world. Meeba Abraham: You have just authored a book on Blender; how did you find the experience? Is this the first book you’ve written? Allan Brito: Writing a book on Blender was quite a challenge for me. Even with the experience of writing tutorials and short articles about Blender, writing a book was not easy! But after a few weeks, I was able to write the chapters naturally and almost on schedule. The biggest challenge for me was to write about a subject that no one else had written about yet. In my first book “Blender 3D – Guia do Usuário” written in Brazilian Portuguese, the challenge was even bigger. When I started writing that book, there weren’t any updated documentation on Blender features. So I had to do a lot of research myself. With this book, the challenge again was to write about something that no one else has ever written. Even with a few short tutorials around, there weren`t any full set of procedures or tips for working with architectural visualization in Blender. The experience was great and I hope this is just the first book in a long series of books! I have a few ideas for writing more books about Blender and I’m already working on some of them. Meeba Abraham: How do you anticipate it will help the Blender community? Is it different to other Blender books? Allan Brito: I believe that a lot of users want to use Blender for architectural visualization but have only found tutorials and books on character modeling and animation. This book was written with architectural visualization in mind. So every example and Blender tool is described specifically with architectural examples. Meeba Abraham: You make regular contributions to www.BlenderNation.com, how did you get involved with the site and what does it offer to the community? Allan Brito: BlenderNation is the comprehensive Web site for Blender related news. So if anyone is curious about what`s going on in the Blender community, the first place to look after the Foundation Web site is BlenderNation. My involvement with BlenderNation began with my writing articles about Blender in Brazilian Portuguese for my own web site (www.allanbrito.com). A few months later, I was invited by Bart Veldhuizen to write a few tutorials and I guess they liked my work! After that I was writing articles for BlenderNation as a Contributor Editor. And I have to say that it`s really great to be a part of it, and keep the Blender community updated. The experience with BlenderNation and the books inspired me to start a new project called Blender 3D Architect (www.blender3darchitect.com) where I write articles on how to use Blender for architectural visualization along with tips and tutorials. Meeba Abraham: Thanks for your time and contributions!
Read more
  • 0
  • 0
  • 3825

article-image-preventing-sql-injection-attacks-your-joomla-websites
Packt
23 Oct 2009
6 min read
Save for later

Preventing SQL Injection Attacks on your Joomla Websites

Packt
23 Oct 2009
6 min read
Introduction Mark Twain once said, "There are only two certainties in life-death and taxes." Even in web security there are two certainties: It's not "if you are attacked", but "when and how" your site will be taken advantage of. There are several types of attacks that your Joomla! site may be vulnerable to such as CSRF, Buffer Overflows, Blind SQL Injection, Denial of Service, and others that are yet to be found. The top issues in PHP-based websites are: Incorrect or invalid (intentional or unintentional) input Access control vulnerabilities Session hijacks and attempts on session IDs SQL Injection and Blind SQL Injection Incorrect or ignored PHP configuration settings Divulging too much in error messages and poor error handling Cross Site Scripting (XSS) Cross Site Request Forgery, that is CSRF (one-click attack) SQL Injections SQL databases are the heart of Joomla! CMS. The database holds the content, the users' IDs, the settings, and more. To gain access to this valuable resource is the ultimate prize of the hacker. Accessing this can gain him/her an administrative access that can gather private information such as usernames and passwords, and can allow any number of bad things to happen. When you make a request of a page on Joomla!, it forms a "query" or a question for the database. The database is unsuspecting that you may be asking a malformed question and will attempt to process whatever the query is. Often, the developers do not construct their code to watch for this type of an attack. In fact, in the month of February 2008, twenty-one new SQL Injection vulnerabilities were discovered in the Joomla! land. The following are some examples presented for your edification. Using any of these for any purpose is solely your responsibility and not mine: Example 1 index.php?option=com_****&Itemid=name&cmd=section&section=-  000/**/union+select/**/000,111,222,      concat(username,0x3a,password),0,     concat(username,0x3a,password)/**/from/**/jos_users/* Example 2 index.php?option=com_****&task=****&Itemid=name&catid=97&aid=- 9988%2F%2A%2A%2Funion%2F%2A%2A%2Fselect/**/ concat(username,0x3a,password),0x3a,password, 0x3a,username,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0/**/ from/**/jos_users/* Both of these will reveal, under the right set of circumstances, the usernames and passwords in your system. There is a measure of protection in Joomla! 1.0.13, with an encryption scheme that will render the passwords useless. However, it does not make sense to allow extensions that are vulnerable to remain. Yielding ANY kind of information like this is unacceptable. The following screenshot displays the results of the second example running on a test system with the vulnerable extension. The two pieces of information are the username that is listed as Author, and the Hex string (partially blurred) that is the hashed password: You can see that not all MD5 hashes can be broken easily. Though it won't be shown here, there is a website available where you enter your hash and it attempts to crack it. It supports several popular hashes. When I entered this hash (of a password) into the tool, I found the password to be Anthony. It's worth noting that this hash and its password are a result of a website getting broken into, prompting the user to search for the "hash" left behind, thus yielding the password. The important news, however, is that if you are using Joomla! 1.0.13 or greater, the password's hash is now calculated with a "salt", making it nearly impossible to break. However, the standard MD5 could still be broken with enough effort in many cases. For more information about salting and MD5 see:http://www.php.net/md5. For an interesting read on salting, you may wish to read this link:www.governmentsecurity.org/forum/lofiversion/index.php/t19179.htm SQL Injection is a query put to an SQL database where data input was expected AND the application does not correctly filter the input. It allows hijacking of database information such as usernames and passwords, as we saw in the earlier example. Most of these attacks are based on two things. First, the developers have coding errors in their code, or they potentially reused the code from another application, thus spreading the error. The other issue is the inadequate validation of input. In essence, it means trusting the users to put in the RIGHT stuff, and not put in queries meant to harm the system. User input is rarely to be trusted for this reason. It should always be checked for proper format, length, and range. There are many ways to test for vulnerability to an SQL Injection, but one of the most common ones is as follows: In some cases, this may be enough to trigger a database to divulge details. This very simplistic example would not work in the login box that is shown. However, if it were presented to a vulnerable extension in a manner such as the following it might work: <FORM action=http://www.vulnerablesite.com/Search.php method=post><input type=hidden name=A value="me' or 1=1--"></FORM> This "posting" method (presented as a very generic exploit and not meant to work per se in Joomla!) will attempt to break into the database by putting forward queries that would not necessarily be noticed. But why 1=1- - ? According to PHP.NET, "It is a common technique to force the SQL parser to ignore the rest of the query written by the developer with-- which is the comment sign in SQL." You might be thinking, "So what if my passwords are hashed? They can get them but they cannot break them!" This is true, but if they wanted it badly, nothing keeps them from doing something such as this: INSERT INTO jos_mydb_users  ('email','password','login_id','full_name')  VALUES ('johndoe@email.com','default','Jdoe','John Doe');--'; This code has a potential if inserted into a query such as this: http://www.yourdomain/vulnerable_extension//index.php?option=com_vulext INSERT INTO jos_mydb_users ('email','password','login_id','full_name') VALUES ('johndoe@email.com','default','Jdoe','John Doe');--'; Again, this is a completely bogus example and is not likely to work. But if you can get an SQL DB to divulge its information, you can get it to "accept" (insert) information it should not as well. 
Read more
  • 0
  • 0
  • 10166

article-image-need-java-business-integration-and-service-engines-netbeans
Packt
23 Oct 2009
6 min read
Save for later

Need for Java Business Integration and Service Engines in NetBeans

Packt
23 Oct 2009
6 min read
In this article, we will discuss the following topics: Need for Java Business Integration (JBI) Enterprise Service Bus Normalized Message Router Service Engines in NetBeans Need for Java Business Integration (JBI) To have a good understanding of Service Engines (a specific type of JBI component), we need to first understand the reason for Java Business Integration. In the business world, not all systems talk the same language. They use different protocols and different forms of communications. Legacy systems in particular can use proprietary protocols for external communication. The advent and acceptance of XML has been greatly beneficial in allowing systems to be easily integrated, but XML itself is not the complete solution. When some systems were first developed, they were not envisioned to be able to communicate with many other systems; they were developed with closed interfaces using closed protocols. This, of course, is fine for the system developer, but makes system integration very difficult. This closed and proprietary nature of enterprise systems makes integration between enterprise applications very difficult. To allow enterprise systems to effectively communicate between each other, system integrators would use vendor-supplied APIs and data formats or agree on common exchange mechanisms between their systems. This is fine for small short term integration, but quickly becomes unproductive as the number of enterprise applications to integrate gets larger. The following figure shows the problems with traditional integration. As we can see in the figure, each third party system that we want to integrate with uses a different protocol. As a system integrator, we potentially have to learn new technologies and new APIs for each system we wish to integrate with. If there are only two or three systems to integrate with, this is not really too much of a problem. However, the more systems we wish to integrate with, the more proprietary code we have to learn and integration with other systems quickly becomes a large problem. To try and overcome these problems, the Enterprise Application Integration (EAI) server was introduced. This concept has an integration server acting as a central hub. The EAI server traditionally has proprietary links to third party systems, so the application integrator only has to learn one API (the EAI server vendors). With this architecture however, there are still several drawbacks. The central hub can quickly become a bottleneck, and because of the hub-and-spoke architecture, any problems at the hub are rapidly manifested at all the clients. Enterprise Service Bus To help solve this problem, leading companies in the integration community (led by Sun Microsystems) proposed the Java Business Integration Specification Request (JSR 208) (Full details of the JSR can be found at http://jcp.org/en/jsr/detail?id=208). JSR 208 proposed a standard framework for business integration by providing a standard set of service provider interfaces (SPIs) to help alleviate the problems experienced with Enterprise Application Integration. The standard framework described in JSR 208 allows pluggable components to be added into a standard architecture and provides a standard common mechanism for each of these components to communicate with each other based upon WSDL. The pluggable nature of the framework described by JSR 208 is depicted in the following figure. It shows us the concept of an Enterprise Service Bus and introduces us to the Service Engine (SE) component: JSR 208 describes a service engine as a component, which provides business logic and transformation services to other components, as well as consuming such services. SEs can integrate Java-based applications (and other resources), or applications with available Java APIs. Service Engine is a component which provides (and consumes) business logic and transformation services to other components. There are various Service Engines available, such as the BPEL service engine for orchestrating business processes, or the Java EE service engine for consuming Java EE Web Services. The Normalized Message Router As we can see from the previous figure, SE's don't communicate directly with each other or with the clients, instead they communicate via the NMR. This is one of the key concepts of JBI, in that it promotes loose coupling of services. So, what is NMR and what is its purpose? NMR is responsible for taking messages from clients and routing them to the appropriate Service Engines for processing. (This is not strictly true as there is another standard JBI component called the Binding Component responsible for receiving client messages. Again, this further enhances the support for loose coupling within JBI, as Service Engines are decoupled from their transport infrastructure). NMR is responsible for passing normalized (that is based upon WSDL) messages between JBI components. Messages typically consist of a payload and a message header which contains any other message data required for the Service Engine to understand and process the message (for example, security information). Again, we can see that this provides a loosely coupled model in which Service Engines have no prior knowledge of other Service Engines. This therefore allows the JBI architecture to be flexible, and allows different component vendors to develop standard based components. Normalized Message Router enables technology for allowing messages to be passed between loosely coupled services such as Service Engines. The figure below gives an overview of the message routing between a client application and two service engines, in this case the EE and SQL service engines. In this figure, a request is made from the client to the JBI Container. This request is passed via NMR to the EE Service Engine. The EE Service Engine then makes a request to the SQL Service Engine via NMR. The SQL Service Engine returns a message to the EE Service Engine again via NMR. Finally, the message is routed back to the client through NMR and JBI framework. The important concept here is that NMR is a message routing hub not only between clients and service engines, but also for intra-communication between different service engines. The entire architecture we have discussed is typically referred to as an Enterprise Service Bus. Enterprise Service Bus (ESB) is a standard-based middleware architecture that allows pluggable components to communicate with each other via a messaging subsystem.
Read more
  • 0
  • 0
  • 2461

article-image-planning-extensions-typo3
Packt
23 Oct 2009
8 min read
Save for later

Planning Extensions in TYPO3

Packt
23 Oct 2009
8 min read
Why is Planning Important? Most open source developers see planning as a boring task. Why plan if one can just go and code? The answer is as simple as the question: The "Go and code" approach does not let us create truly optimal code. Portions of code have to be changed while other portions are written. They often lead to redundant code or uninitialized variables, partially covered conditions, and wrong return results. Code gets a "do not touch" reputation because changing anything renders the whole project unstable. Often the code works, but the project is more a failure than a success because it cannot be extended or re-used. Another reason for planning is the ease of bug fixing and the costs associated with it. Open source developers often do not think about it until they start selling their services or work to commercial companies. As shown by recent studies, the cost of problem fixing grows rapidly toward the end of the project. The cost is minimal when development has not started yet, and the person in charge just collects requirements. When requirements are collected and a programmer (or a team of programmers) starts to think how to implement these requirements, a change of requirements, or fixing a problem in the requirements still does not cost much. But it may already be difficult for developers if they came to a certain implementation approach after reviewing requirements. Things become worse at the development stage. Imagine that the selected approach was wrong and it was uncovered close to the end of development. Lots of time is lost, and work may have to start from the beginning. Now imagine what happens if the project is released to the customer and the customer says that the outcome of the project does not work as expected (something was implemented differently (as compared to expectations), or something was not implemented at all). The cost of fixing is likely to be high and overshoot the budget. Next, imagine what would happen if problems occurred when a project went live. After reading the previous paragraph, some developers may ask how the situation applies to non-commercial development, as there is a false perception that there are no costs associated with it (at least, no direct costs). But, the costs exist! And often they are much more sensitive than financial costs. The cost in non-commercial development is reputation. If a developer's product does not work well or does not work at all or it has obvious flaws, the general opinion about the developer may become bad ("cannot trust his code"). Developers will also have troubles improving because often they do not understand what has gone wrong. But the answer is near. Do not rush! Plan it well! You may even think of something about the future code, and then start coding only when the picture is clear. Planning is an important part of software development. While freelancers can usually divide their time freely between planning and implementation, many corporate developers often do not have such freedom. And even worse, many managers still do not see planning as a necessary step in software development. This situation is well explained in The parable of the two programmers, which readers of this book are encouraged to read in full. When it comes to TYPO3, planning is more important than an average application. TYPO3 is very complex, and its implementation is also complex. Without planning, programmers will most likely have to change their already written code to fix unforeseen problems therefore, good planning for TYPO3 extensions is extremely important. But let us move on and see how to plan an extension. How to Plan There are several stages in planning. Typically, each stage answers one or more important questions about development. TYPO3 developers should think about at least three stages: Gathering requirements Implementation planning Documentation planning Of course, each project is unique and has other stages. But these three stages generally exist in every project. Gathering Requirements The first thing that a developer needs to know is what his/her extension will do. While it sounds pretty obvious, not many extension authors know exactly what functionality the extension has in the end. It evolves over time, and often the initial idea is completely different from the final implementation. Predictably, neither the original nor the final is done well. In the other case, when extension features are collected, though planned and implemented according to plan, they usually fit well together. So, the very first thing to do when creating an extension is to find out what that extension should do. This is called gathering requirements. For non-commercial extensions, gathering requirements simply means writing down what each extension should do. For example, for a news extension, it may be: Show list of news sorted by date Show list of latest news Show news archive Show only a small amount of text in news list view As we have seen, gathering requirements looks easier than it actually is. The process, however, may become more complex when an extension is developed for an external customer. Alan Cooper, in his famous About Face book, shows how users, architects, and developers see the same product. From the user's perspective, it looks like a perfect circle. An architect sees something closer to an octagon. A developer creates something that looks like a polygon with many segments connected at different degrees. These differences always exist and each participating party is interested in minimizing them. A developer must not be afraid of asking questions. The cleaner picture he/she has, the better he/she will understand the customer's requirements. Implementation Planning When the requirements are gathered, it is necessary to think which blocks an extension will have. It may be blocks responsible for data fetching, presentation, conversion, and so on. In the case of TYPO3 extension implementation, planning should result in a list of Frontend (FE) plugins, Backend (BE) modules, and standalone classes. The purpose of each plug-in, module, and/or class must be clear. When thinking of FE plugins, caching issues must be taken into account. While most of the output can be cached to improve TYPO3 performance, forms processing should not be cached. Some extensions completely prevent caching of the page when processing forms. But there is a better approach, a separate FE plug-in from the non-cached output. BE modules must take into account the ease of use. Standard BE navigation is not very flexible, and this must be taken into account when planning BE modules. Certain functionalities can be moved to separate classes. This includes common functions and any public APIs that an extension provides to the other extensions. Hooks or "user functions" are usually placed in separate classes depending on the functional zone or hooked class. Documentation Planning A good extension always comes with documentation. Documentation should also be planned. Typically, manuals for extensions are created using standard templates, which have standard sections defined. While this simplifies documentation writing for extension developers, they still have to plan what they will put into these sections. TYPO3-Specific Planning There are several planning issues specific to TYPO3. Developers must take care of them before the actual development. Extension Keys Each extension must have a unique key. Extension keys can be alphanumeric and contain underscore characters. It may not start with a digit, the letter u, or the test_ prefix. However, not every combination of these symbols makes a good extension key. An extension key must be descriptive but not too long. Having personal or company prefixes is not forbidden but is not recommended. Underscores should be avoided. Abbreviations should be avoided as well, because they often do not make sense for other users. Examples of good extension keys are: news comments usertracker loginbox Examples of bad extension keys are: news_extension mycorp_ustr myverygoodextensionthatdoesalotofthings mvgetdalot john_ext div2007 Database Structure Most TYPO3 extensions use a database to load and/or store their own data. Changing the data structure during application development may seriously slow down development, or may even cause damage to data if some data is already entered into the system. Therefore, it is extremely important to think about an extension's data structure well in advance. Such thinking requires knowledge about how TYPO3 database tables are organized. Tables in TYPO3 database must have certain structures to be properly managed by TYPO3. If a table does not fulfill TYPO3 requirements, users may see error messages in the BE (especially in the Web | List module), and data may become corrupted. Every record in every TYPO3 table belongs to a certain page inside TYPO3. TYPO3 has a way to identify which page the record belongs to.
Read more
  • 0
  • 0
  • 1502

article-image-adapting-user-devices-using-mobile-web-technology
Packt
23 Oct 2009
10 min read
Save for later

Adapting to User Devices Using Mobile Web Technology

Packt
23 Oct 2009
10 min read
Luigi's Pizza On The Run mobile shop is working well now. And he wants to adapt it to different mobile devices. Let's look at the following: Understanding the Lowest Common Denominator method Finding and comparing features of different mobile devices Deciding to adapt or not Adapting and progressively enhancing POTR application using Wireless Abstraction Library Detecting device capabilities Evaluating tools that can aid in adaptation Moving your blog to the mobile web By the end of this article, you will have a strong foundation in adapting to different devices. What is Adaptation? Adaptation, sometimes called multiserving, means delivering content as per each user device's capabilities. If the visiting device is an old phone supporting only WML, you will show a WML page with Wireless Bitmap (wbmp) images. If it is a newer XHTML MP-compliant device, you will deliver an XHTML MP version, customized according to the screen size of the device. If the user is on iMode in Japan, you will show a Compact HTML (cHTML) version that's more forgiving than XHTML. This way, users get the best experience possible on their device. Do I Need Adaptation? I am sure most of you are wondering why you would want to create somany different versions of your mobile site? Isn't following the XHTML MPstandard enough? On the Web, you could make sure that you followed XHTML and the site will work in all browsers. The browser-specific quirks are limited and fixes are easy. However, in the mobile world, you have thousands of devices using hundreds of different browsers. You need adaptation precisely for that reason! If you want to serve all users well, you need to worry about adaptation. WML devices will give up if they encounter a <b> tag within an <a> tag. Some XHTML MP browsers will not be able to process a form if it is within a table. But a table within a form will work just fine. If your target audience is limited, and you know that they are going to use a limited range of browsers, you can live without adaptation. Can't I just Use Common Capabilities and Ignore the Rest? You can. Finding the Lowest Common Denominator (LCD) of the capabilities of target devices, you can design a site that will work reasonably well in all devices. Devices with better capabilities than LCD will see a version that may not be very beautiful but things will work just as well. How to Determine the LCD? If you are looking for something more than the W3C DDC guidelines, you may be interested in finding out the capabilities of different devices to decide on your own what features you want to use in your application. There is a nice tool that allows you to search on device capabilities and compare them side by side. Take a look at the following screenshot showing mDevInf (http://mdevinf.sourceforge.net/) in action, showing image formats supported on a generic iMode device. You can search for devices and compare them, and then come to a conclusion about features you want to use. This is all good. But when you want to cater to wider mobile audience, you must consider adaptation. You don't want to fight with browser quirks and silly compatibility issues. You want to focus on delivering a good solution. Adaptation can help you there. OK, So How do I Adapt? You have three options to adapt: Design alternative CSS: this will control the display of elements and images. This is the easiest method. You can detect the device and link an appropriate CSS file. Create multiple versions of pages: redirect the user to a device-specific version. This is called "alteration". This way you get the most control over what is shown to each device. Automatic Adaptation: create content in one format and use a tool to generate device-specific versions. This is the most elegant method. Let us rebuild the pizza selection page on POTR to learn how we can detect the device and implement automatic adaptation. Fancy Pizza Selection Luigi has been asking to put up photographs of his delicious pizzas on the mobile site, but we didn't do that so far to save bandwidth for users. Let us now go ahead and add images to the pizza selection page. We want to show larger images to devices that can support them. Review the code shown below. It's an abridged version of the actual code. <?php include_once("wall_prepend.php"); ?> <wall:document><wall:xmlpidtd /> <wall:head> <wall:title>Pizza On The Run</wall:title> <link href="assets/mobile.css" type="text/css" rel="stylesheet" /> </wall:head> <wall:body> <?php echo '<wall:h2>Customize Your Pizza #'.$currentPizza.':</wall:h2> <wall:form enable_wml="false" action="index.php" method="POST"> <fieldset> <wall:input type="hidden" name="action" value="order" />'; // If we did not get the total number of pizzas to order, // let the user select if ($_REQUEST["numPizza"] == -1) { echo 'Pizzas to Order: <wall:select name="numPizza">'; for($i=1; $i<=9; $i++) { echo '<wall:option value="'.$i.'">'.$i.'</wall:option>'; } echo '</wall:select><wall:br/>'; } else { echo '<wall:input type="hidden" name="numPizza" value="'.$_REQUEST["numPizza"].'" />'; } echo '<wall:h3>Select the pizza</wall:h3>'; // Select the pizza $checked = 'checked="checked"'; foreach($products as $product) { // Show a product image based on the device size echo '<wall:img src="assets/pizza_'.$product[ "id"].'_120x80.jpg" alt="'.$product["name"].'"> <wall:alternate_img src="assets/pizza_'.$product[ "id"].'_300x200.jpg" test="'.($wall->getCapa( 'resolution_width') >= 200).'" /> <wall:alternate_img nopicture="true" test="'.( !$wall->getCapa('jpg')).'" /> </wall:img><wall:br />'; echo '<wall:input type="radio" name="pizza[ '.$currentPizza.']" value="'.$product["id"].'" '.$checked.'/>'; echo '<strong>'.$product["name"].' ($'.$product[ "price"].')</strong> - '; echo $product["description"].'<wall:br/>'; $checked = ''; } echo '<wall:input type="submit" class="button" name= "option" value="Next" /> </fieldset></wall:form>'; ?> <p><wall:a href="?action=home">Home</wall:a> - <wall:caller tel="+18007687669"> +1-800-POTRNOW</wall:caller></p> </wall:body> </wall:html> What are Those <wall:*> Tags? All those <wall:*> tags are at the heart of adaptation. Wireless Abstraction Library (WALL) is an open-source tag library that transforms the WALL tags into WML, XHTML, or cHTML code. E.g. iMode devices use <br> tag and simply ignore <br />. WALL will ensure that cHTML devices get a <br> tag and XHTML devices get a <br /> tag. You can find a very good tutorial and extensive reference material on WALL from: http://wurfl.sourceforge.net/java/wall.php. You can download WALL and many other tools too from that site. WALL4PHP—a PHP port of WALL is available from http://wall.laacz.lv/. That's what we are using for POTR. Let's Make Sense of This Code! What are the critical elements of this code? Most of it is very similar to standard XHTML MP. The biggest difference is that tags have a "wall:" prefix. Let us look at some important pieces: The wall_prepend.php file at the beginning loads the WALL class, detects the user's browser, and loads its capabilities. You can use the $wall object in your code later to check device capabilities etc. <wall:document> tells the WALL parser to start the document code. <wall:xmlpidtd /> will insert the XHTML/WML/CHTML prolog as required. This solves part of the headache in adaptation. The next few lines define the page title and meta tags. Code that is not in <wall:*> tags is sent to the browser as is. The heading tag will render as a bold text on a WML device. You can use many standard tags with WALL. Just prefix them with "wall:". We do not want to enable WML support in the form. It requires a few more changes in the document structure, and we don't want it to get complex for this example! If you want to support forms on WML devices, you can enable it in the <wall:form> tag. The img and alternate_img tags are a cool feature of WALL. You can specify the default image in the img tag, and then specify alternative images based on any condition you like. One of these images will be picked up at run time. WALL can even skip displaying the image all together if the nopicture test evaluates to true. In our code, we show a 120x100 pixels images by default, and show a larger image if the device resolution is more than 200 pixels. As the image is a JPG, we skip showing the image if the device cannot support JPG images. The alternate_img tag also supports showing some icons available natively on the phone. You can refer to the WALL reference for more on this. Adapting the phone call link is dead simple. Just use the <wall:caller> tag. Specify the number to call in the tel attribute, and you are done. You can also specify what to display if the phone does not support phone links in alt attribute. When you load the URL in your browser, WALL will do all the heavy liftingand show a mouth-watering pizza—a larger mouth-watering pizza if you have a large screen! Can I Use All XHTML Tags? WALL supports many XHTML tags. It has some additional tags to ease menu display and invoke phone calls. You can use <wall:block> instead of code <p> or <div> tags because it will degrade well, and yet allow you to specify CSS class and id. WALL does not have tags for tables, though it can use tables to generate menus. Here's a list of WALL tags you can use: a, alternate_img, b, block, body, br, caller, cell, cool_menu, cool_menu_css, document, font, form, h1, h2, h3, h4, h5, h6, head, hr, i, img, input, load_capabilities, marquee, menu, menu_css, option, select, title, wurfl_device_id, xmlpidtd. Complete listings of the attributes available with each tag, and their meanings are available from: http://wurfl.sourceforge.net/java/refguide.php. Complete listings of the attributes available with each tag, and their meanings are available from: http://wurfl.sourceforge.net/java/refguide.php. Will This Work Well for WML? WALL can generate WML. WML itself has limited capabilities so you will be restricted in the markup that you can use. You have to enclose content in <wall:block> tags and test rigorously to ensure full WML support. WML handles user input in a different way and we can't use radio buttons or checkboxes in forms. A workaround is to change radio buttons to a menu and pass values using the GET method. Another is to convert them to a select drop down. We are not building WML capability in POTR yet. WALL is still useful for us as it can support cHTML devices and will automatically take care of XHTML implementation variations in different browsers. It can even generate some cool menus for us! Take a look at the following screenshot.
Read more
  • 0
  • 0
  • 2786
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-routing-rules-asterisknow-calling-rules-tables
Packt
23 Oct 2009
7 min read
Save for later

Routing Rules in AsteriskNOW - The Calling Rules Tables

Packt
23 Oct 2009
7 min read
An interesting feature of a PBX is the ability to support multiple dial plans, meaning that you are able to create various dial-plan logics, associate different calling rules to each dial plan, and assign users to specific dial plan. Schematically speaking, the relation between the three can be illustrated as follows: Essentially, from AsteriskNOW's point of view, any dial attempt that doesn't match a Calling Rule will be considered an internal call, and thus, AsteriskNOW will try to route the call to an internal AsteriskNOW resource—e.g. another extension or an AsteriskNOW feature code. Most telephony engineers and carrier support engineers refer to "Calling Rules" as "Routing Rules". From this point onwards in the book "Calling Rules" will be referred to as "Routing Rules". Managing Routing Rules with AsteriskNOW At this point, after initially configuring your service providers and initial routing rules, your "Routing Rules" table should look as follows: Edit one of these rules to get acquainted with the call rule dialog box. Click the Edit link of the all_outbound rule (rule 1). The following dialog box should appear on your screen: Every call made from an IP phone connected to the PBX is processed by the routing rules. The processing is performed in the following order: AsteriskNOW grabs the dialed number and tries to match it to the prefix defined in the Routing Rule. In this dialog, the prefix is 9. It then verifies the number of digits suffixing the prefix. In this example, any number of digits that is 3 or more is considered a valid number to be assigned to this route. Now, before actually routing the call to the designated service provider, AsteriskNOW can remove prefixes and/or prefix numbers to the dialed number. In this example it will only remove a single prefixing digit (9) and then pass the call to your service provider–Ports 1,2,3. The above process happens for every call that is made by a phone connected to the PBX. If the process fails all the rules defined in the Routing Table, AsteriskNOW assumes that the call is supposed to be routed internally. If internal routing fails, the call will fail and a fast-busy tone will be heard from your IP phone. Some IP phones also indicate the SIP error message that was received. If routing fails, the normal error that you may encounter would be error 404 – NOT FOUND. Manually Editing Dial-Plan Logic Additional information about Asterisk configuration filesWhile this book deals with AsteriskNOW—aimed to provide a simple, fast solution for managing the Asterisk Open Source PBX—it does so without getting into the inner workings of Asterisk's configuration files. Additional information about the configuration files, their format, usage, and various available options is available at the voip-info wiki, located at: http://www.voip-info.org/wiki/view/Asterisk+config+files. Manually editing AsteriskNOW dial-plan files isn't recommended; however, in some extreme situations it can't be avoided (especially while creating special applications with AsteriskNOW). For this specific reason, AsteriskNOW includes a facility to edit the configuration files manually. Click the File Editor menu option; the following screen should appear:   The extensions.conf file should interest you. Using the drop-down file selector, select the extensions.conf file for editing. The following screen should be observed: Now, if you were to click one of the lightly-shaded areas (light green on your screen); a text editing box will open to enable you to edit the file. You're probably wondering at this point—"where is routing logic located?" Scroll down the file and seek the section designated as numberplan-custom-1, which designates your DialPlan1 dial plan. If you were to create an additional dial plan using the GUI, its designation in the extensions.conf file would be numberplan-custom-2. Examine the following section: In the above section lines indicated by the exten directive, indicate a dial-plan activity. The exten directive is then followed by some form of well formatted number string, followed by a number indicating the sequence number of the directive, then followed by an Asterisk operational command—in our case, the Macro command. Asterisk and AsteriskNOW include over 150 different applications; explanation of each and every application is beyond the scope of this book. Visit the Asterisk Wiki page at http://www.voip-info.org/wiki/index.php?page=Asterisk for more information about Asterisk and AsteriskNOW applications. The way the dial plan analyzes dialed numbers is explained next. Consider one of the dialing rules: exten=_9XXX!,1,Macro(trunkdial,${trunk_1}/${EXTEN:1}) The interesting portion of this line is the _9XXX!. The following is an extract from an Asterisk documentation: ; Extension names may be numbers, letters, or combinations; thereof. If an extension name is prefixed by a ‘_'; character, it is interpreted as a pattern rather than a; literal. In patterns, some characters have special meanings:;; X - any digit from 0-9; Z - any digit from 1-9; N - any digit from 2-9; [1235-9] - any digit in the brackets (in this example, 1,2,3,5,6,7,8,9); . - wildcard, matches anything remaining (e.g. _9011. matches; anything starting with 9011 excluding 9011 itself); ! - wildcard, causes the matching process to complete as soon as; it can unambiguously determine that no other matches are possible;; For example the extension _NXXXXXX would match normal 7 digit dialings,; while _1NXXNXXXXXX would represent an area code plus phone number; preceded by a one.;; Each step of an extension is ordered by priority, which must; always start with 1 to be considered a valid extension. The priority; "next" or "n" means the previous priority plus one, regardless of whether; the previous priority was associated with the current extension or not.; The priority "same" or "s" means the same as the previously specified; priority, again regardless of whether the previous entry was for the; same extension. Priorities may be immediately followed by a plus sign; and another integer to add that amount (most useful with ‘s' or ‘n').; Priorities may then also have an alias, or label, in; parenthesis after their name which can be used in goto situations As this text suggest, the underscore marking (_) indicates the start of a pattern matching rule. This is then followed by a form of expression indicating the pattern to match. In the example, the pattern match is _9XXX!, so, interpreting this according to the documentation: _9: Indicates any number that is prefixed with the digit 9. This corresponds to the first routing rule. XXX: Indicates 3 digits, ranging from 0 to 9. This corresponds to the second portion of the routing rule. !: Indicates to match as soon as there is no other rule that may apply, thus closing the matching process. For example, in accordance to the above points, the number 912345 will match the rule indicated above and will simply activate the Macro application. However, bearing in mind that the routing requires at least 3 digits to follow the prefix, the number 912 will not match our above routing rule. At this point, the working of the Macro application is not explained; however, the empirical knowledge of its existence is enough. Summary In this article you learned what routing rules are and how they are processed within the AsteriskNOW operational model. Understanding the way routing rules work is imperative to configure your PBX for optimal usage of outbound connections.
Read more
  • 0
  • 0
  • 3945

article-image-photoshop-foundation-difference-between-vector-and-bitmap-graphics
Packt
23 Oct 2009
4 min read
Save for later

Photoshop Foundation - The Difference between Vector and Bitmap Graphics

Packt
23 Oct 2009
4 min read
Introduction Welcome to the first in a new series of articles on Photoshop - the Photoshop Foundations series. The aim of this series is to give both beginners and more experienced users all the information they need to use Photoshop as efficiently as possible. Photoshop is a huge application, and there is usually more than one way to look at a given subject, or perform a certain action. This series aims to both, guide you through the more confusing aspects of Photoshop and show you the very best ways to use this application. In this first article we are going to look at the difference between vector and bitmap graphics, which is one of the most important principles to understand when working with graphics on a computer, inside or outside of Photoshop. Although Photoshop primarily is a bitmap image editor, it is capable of handling vector graphics to a certain extent. This can be a little confusing for people new to creating graphics on a computer, but by the end of this article you should have a clear idea of the difference between these two types of graphics. Bitmap Graphics Bitmap graphics are made up of colored pixels. Pixels are very small rectangles (usually square, although in some video applications they are wider than they are tall) of varying colors that once put together give you an image. You can see from the example below that zooming in on a bitmap image reveals the pixels that make up the image when viewed at 100%.   Bitmap graphics are usually (but not always) photographic in nature, capable of subtle graduated tones - often in the range of millions of colors per image. The problem with bitmap graphics is that they don't enlarge well as Photoshop needs to guess what color the extra pixels should be - this can result is loss of definition and a dramatic lowering in quality, depending on how much you enlarge the image. Common file formats for bitmap image data include GIF, JPEG and PNG for Internet usage and TIFF for print usage. As you can see from the example below, physically enlarging an image will degrade quality. Pixels are also used to display the image on your computer screen. Common pixel dimensions of computer displays are 1024 wide by 768 high and 1600 wide by 1200 high. The size of a bitmap graphic when viewed on your computer screen is defined by the number of pixels that make up the image - so an image that is 50 pixels wide will look very small on your screen at 100% viewing percentage, whereas an image that is 4000 pixels wide will be larger than your screen at 100% viewing percentage. The printable dimensions of an image are defined by the DPI (dots per inch) - this information is invisibly embedded in the image file. Digital cameras often embed information such as this, that may include the conditions the image was taken in, and even the camera model used. This information is not actually visible in the image, and requires software such as Photoshop to read it. You should not confuse the output DPI of your printer with this figure, which may range from 600-2400DPI - this refers to the density of the dots of ink laid down on the page by the printer. You don't have to prepare your images to 2400 DPI to get the best results - in fact doing so will significantly slow down printing as your file could potentially be huge! Often an image DPI in the range of 175-250 will give very good results on home printers. Images prepared for high quality commercial print are usually prepared at 300 DPI for up to A3 in size; whereas very large images (for instance on billboards) can be as low as 50 DPI, as they are not made to be viewed as closely as a magazine or small poster. There is no need to go above 300 DPI when creating images as you will yield virtually no improvement in output quality, only increasing the size of your file when saved. It is easy to understand the relationship between pixel dimensions and DPI - put simply, the DPI is how many pixels will be printed in an inch - so you could actually think of DPI as PPI (pixels per inch). Indeed, many experts believe this to be the true definition of DPI, and that Photoshop should refer to it as such. However, the term DPI is used throughout the professional print industry, so this is why it is referred to as DPI in Photoshop, not PPI.
Read more
  • 0
  • 0
  • 12849

article-image-chatroom-application-using-dwr-java-framework
Packt
23 Oct 2009
19 min read
Save for later

Chatroom Application using DWR Java Framework

Packt
23 Oct 2009
19 min read
Starting the Project and Configuration We start by creating a new project for our chat room, with the project name DWRChatRoom. We also need to add the dwr.jar file to the lib directory and enable DWR in the web.xml file. The following is the source code of the dwr.xml file. <?xml ver sion="1.0" encoding="UTF-8"?> <!DOCTYPE dwr PUBLIC "-//GetAhead Limited//DTD Direct Web Remoting 2.0//EN" "http://getahead.org/dwr/dwr20.dtd"> <dwr> <allow> <create creator="new" javascript="Login"> <param name="class" value="chatroom.Login" /> </create> <create creator="new" javascript="ChatRoomDatabase"> <param name="class" value="chatroom.ChatRoomDatabase" /> </create> </allow> </dwr> The source code for web.xml is as follows: <?xml ver sion="1.0" encoding="UTF-8"?> <web-app xsi_schemaLocation="http://java. sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_ 5.xsd" id="WebApp_ID" version="2.5"> <display-name>DWRChatRoom</display-name> <servlet> <display-name>DWR Servlet</display-name> <servlet-name>dwr-invoker</servlet-name> <servlet-class> org.directwebremoting.servlet.DwrServlet </servlet-class> <init-param> <param-name>debug</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>activeReverseAjaxEnabled</param-name> <param-value>true</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>dwr-invoker</servlet-name> <url-pattern>/dwr/*</url-pattern> </servlet-mapping> <welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file> <welcome-file>default.html</welcome-file> <welcome-file>default.htm</welcome-file> <welcome-file>default.jsp</welcome-file> </welcome-file-list></web-app> Developing the User Interface The next step we do is to create files for presentation: style sheet and HTML/JSP files. The style sheet, loginFailed.html, and index.jsp files are required for the application. The source code of the style sheet is as follows: body{margin:0;padding:0;line-height: 1.5em;}b{font-size: 110%;}em{color: red;}#topsection{background: #EAEAEA;height: 90px; /*Height of top section*/}#topsection h1{margin: 0;padding-top: 15px;}#contentwrapper{float: left;width: 100%;}#contentcolumn{margin-left: 200px; /*Set left margin to LeftColumnWidth*/}#leftcolumn{float: left;width: 200px; /*Width of left column*/margin-left: -100%;background: #C8FC98;}#footer{clear: left;width: 100%;background: black;color: #FFF;text-align: center;padding: 4px 0;}#footer a{color: #FFFF80;}.innertube{margin: 10px; /*Margins for inner DIV inside each column (to provide padding)*/margin-top: 0;} Our first page is the login page. It is located in the WebContent directory and it is named index.jsp. The source code for the page is given as follows: <%@ page language="java" contentType="text/html; charset=ISO-8859-1"    pageEncoding="ISO-8859-1"%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>Book Authoring</title><script type='text/javascript' src='/DWRChatroom/dwr/interface/Login.js'></script><script type='text/javascript' src='/DWRChatroom/dwr/engine.js'></script><script type='text/javascript' src='/DWRChatroom/dwr/util.js'></script>  <script type="text/javascript">function login(){  var userNameInput=dwr.util.byId('userName');  var userName=userNameInput.value;  Login.doLogin(userName,loginResult);}function loginResult(newPage){  window.location.href=newPage;}</script></head><body><h1>Book Authoring Sample</h1><table cellpadding="0" cellspacing="0"><tr><td>User name:</td><td><input id="userName" type="text" size="30"></td></tr><tr><td>&nbsp;</td><td><input type="button" value="Login" onclick="login();return false;"></td></tr></table></body></html> The login screen uses the DWR functionality to process the user login (the Java classes are presented after the web pages). The loginResults function opens either the failure page or the main page based on the result of the login operation. If the login was unsuccessful, a very simple loginFailed.html page is shown to the user, the source code for which is as follows: <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html;                                         charset=ISO-8859-1"><title>Login failed</title></head><body><h2>Login failed.</h2></body></html> The main page, mainpage.jsp, includes all the client-side logic of our ChatRoom application. The source code for the page is as follows: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html lang="en" xml_lang="en"><head><meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /><title>Chatroom</title><link href="styles.css" rel="stylesheet" type="text/css" /><%   if (session.getAttribute("username") == null         || session.getAttribute("username").equals("")) {      //if not logged in and trying to access this page      //do nothing, browser shows empty page      return;   }%><script type='text/javascript' src='/DWRChatRoom/dwr/interface/Login.js'></script><script type='text/javascript' src='/DWRChatRoom/dwr/interface/ChatRoomDatabase.js'></script><script type='text/javascript' src='/DWRChatRoom/dwr/engine.js'></script><script type='text/javascript' src='/DWRChatRoom/dwr/util.js'></script>  <script type="text/javascript">dwr.engine.setActiveReverseAjax(true);function logout(){  Login.doLogout(showLoginScreen);}function showLoginScreen(){  window.location.href='index.jsp';}function showUsersOnline(){  var cellFuncs = [          function(user) {             return '<i>'+user+'</i>';          }          ];    Login.getUsersOnline({    callback:function(users)     {      dwr.util.removeAllRows('usersOnline');      dwr.util.addRows( "usersOnline",users, cellFuncs,                                    { escapeHtml:false });    }    });}function getPreviousMessages(){    ChatRoomDatabase.getChatContent({    callback:function(messages)     {      var chatArea=dwr.util.byId('chatArea');      var html="";      for(index in messages)      {         var msg=messages[index];         html+=msg;      }      chatArea.innerHTML=html;      var chatAreaHeight = chatArea.scrollHeight;      chatArea.scrollTop = chatAreaHeight;    }    });}function newMessage(message){  var chatArea=dwr.util.byId('chatArea');  var oldMessages=chatArea.innerHTML;  chatArea.innerHTML=oldMessages+message;    var chatAreaHeight = chatArea.scrollHeight;  chatArea.scrollTop = chatAreaHeight;}function sendMessageIfEnter(event){  if(event.keyCode == 13)  {    sendMessage();  }}function sendMessage(){    var message=dwr.util.byId('messageText');    var messageText=message.value;    ChatRoomDatabase.postMessage(messageText);    message.value='';}</script></head><body onload="showUsersOnline();"><div id="maincontainer"><div id="topsection"><div class="innertube"><h1>Chatroom</h1><h4>Welcome <i><%=(String) session.getAttribute("username")%></i></h4></div></div><div id="contentwrapper"><div id="contentcolumn"><div id="chatArea" style="width: 600px; height: 300px; overflow: auto"></div><div id="inputArea"><h4>Send message</h4><input id="messageText" type="text" size="50"  onkeyup="sendMessageIfEnter(event);"><input type="button" value="Send msg"                                                      onclick="sendMessage();"></div></div></div><div id="leftcolumn"><div class="innertube"><table cellpadding="0" cellspacing="0">  <thead>    <tr>      <td><b>Users online</b></td>    </tr>  </thead>  <tbody id="usersOnline">  </tbody></table><input id="logoutButton" type="button" value="Logout"  onclick="logout();return false;"></div></div><div id="footer">Stylesheet by <a  href="http://www.dynamicdrive.com/style/">Dynamic Drive CSSLibrary</a></div></div><script type="text/javascript">getPreviousMessages();</script></body></html> The first chat-room-specific JavaScript function is getPreviousMessages(). This function is called at the end of mainpage.jsp, and it retrieves previous chat messages for this chat room. The newMessage() function is called by the server-side Java code when a new message is posted to the chat room. The function also scrolls the chat area automatically to show the latest message. The sendMessageIfEnter() and sendMessage() functions are used to send user messages to the server. There is the input field for the message text in the HTML code, and the sendMessageIfEnter() function listens to onkeyup events in the input field. If the user presses enter, the sendMessage() function is called to send the message to the server. The HTML code includes the chat area of specified size and with automatic scrolling. Developing the Java Code There are several Java classes in the application. The Login class handles the user login and logout and also keeps track of the logged-in users. The source code of the Login class is as follows: package chatroom;import java.util.Collection;import java.util.List;import javax.servlet.ServletContext;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpSession;import org.directwebremoting.ScriptSession;import org.directwebremoting.ServerContext;import org.directwebremoting.ServerContextFactory;import org.directwebremoting.WebContext;import org.directwebremoting.WebContextFactory;import org.directwebremoting.proxy.ScriptProxy;public class Login {   public Login() {   }      public String doLogin(String userName) {      UserDatabase userDb=UserDatabase.getInstance();      if(!userDb.isUserLogged(userName)) {         userDb.login(userName);         WebContext webContext= WebContextFactory.get();         HttpServletRequest request = webContext.getHttpServletRequest();         HttpSession session=request.getSession();         session.setAttribute("username", userName);         String scriptId = webContext.getScriptSession().getId();         session.setAttribute("scriptSessionId", scriptId);         updateUsersOnline();         return "mainpage.jsp";      }      else {         return "loginFailed.html";      }   }      public void doLogout() {      try {         WebContext ctx = WebContextFactory.get();         HttpServletRequest request = ctx.getHttpServletRequest();         HttpSession session = request.getSession();         Util util = new Util();         String userName = util.getCurrentUserName(session);         UserDatabase.getInstance().logout(userName);         session.removeAttribute("username");         session.removeAttribute("scriptSessionId");         session.invalidate();      } catch (Exception e) {         System.out.println(e.toString());      }      updateUsersOnline();   }      private void updateUsersOnline() {      WebContext webContext= WebContextFactory.get();      ServletContext servletContext = webContext.getServletContext();      ServerContext serverContext = ServerContextFactory.get(servletContext);      webContext.getScriptSessionsByPage("");      String contextPath = servletContext.getContextPath();      if (contextPath != null) {         Collection<ScriptSession> sessions =                            serverContext.getScriptSessionsByPage                                            (contextPath + "/mainpage.jsp");         ScriptProxy proxy = new ScriptProxy(sessions);         proxy.addFunctionCall("showUsersOnline");      }   }      public List<String> getUsersOnline() {      UserDatabase userDb=UserDatabase.getInstance();      return userDb.getLoggedInUsers();   }} The following is the source code of the UserDatabase class package chatroom;import java.util.List;import java.util.Vector;//this class holds currently logged in users//there is no persistencepublic class UserDatabase {      private static UserDatabase userDatabase=new UserDatabase();      private List<String> loggedInUsers=new Vector<String>();      private UserDatabase() {}      public static UserDatabase getInstance() {      return userDatabase;   }      public List<String> getLoggedInUsers() {      return loggedInUsers;   }      public boolean isUserLogged(String userName) {      return loggedInUsers.contains(userName);    }      public void login(String userName) {      loggedInUsers.add(userName);   }      public void logout(String userName) {      loggedInUsers.remove(userName);   }} The Util class is used by the Login class, and it provides helper methods for the sample application. The source code for the Util class is as follows: package chatroom;import java.util.Hashtable;import java.util.Map;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpSession;import org.directwebremoting.WebContext;import org.directwebremoting.WebContextFactory;public class Util {      public Util() {         }      public String getCurrentUserName() {      //get user name from session      WebContext ctx = WebContextFactory.get();      HttpServletRequest request = ctx.getHttpServletRequest();      HttpSession session=request.getSession();      return getCurrentUserName(session);   }   public String getCurrentUserName(HttpSession session) {      String userName=(String)session.getAttribute("username");      return userName;   }} The logic for the server-side chat room functionality is in the ChatRoomDatabase class. The source code for the ChatRoomDatabase is as follows: package chatroom;import java.util.Collection;import java.util.Date;import java.util.List;import java.util.Vector;import javax.servlet.ServletContext;import org.directwebremoting.ScriptSession;import org.directwebremoting.ServerContext;import org.directwebremoting.ServerContextFactory;import org.directwebremoting.WebContext;import org.directwebremoting.WebContextFactory;import org.directwebremoting.proxy.ScriptProxy;public class ChatRoomDatabase {   private static List<String> chatContent = new Vector<String>();   public ChatRoomDatabase() {   }   public void postMessage(String message) {      String user = (new Util()).getCurrentUserName();      if (user != null) {         Date time = new Date();         StringBuffer sb = new StringBuffer();         sb.append(time.toString());         sb.append(" <b><i>");         sb.append(user);         sb.append("</i></b>:  ");         sb.append(message);         sb.append("<br/>");         String newMessage=sb.toString();         chatContent.add(newMessage);         postNewMessage(newMessage);      }   }   public List<String> getChatContent() {      return chatContent;   }   private ScriptProxy getScriptProxyForSessions() {      WebContext webContext = WebContextFactory.get();      ServletContext servletContext = webContext.getServletContext();      ServerContext serverContext = ServerContextFactory.get(servletContext);      webContext.getScriptSessionsByPage("");      String contextPath = servletContext.getContextPath();      if (contextPath != null) {         Collection<ScriptSession> sessions = serverContext               .getScriptSessionsByPage(contextPath + "/mainpage.jsp");         ScriptProxy proxy = new ScriptProxy(sessions);         return proxy;      }      return null;   }   public void postNewMessage(String newMessage) {      ScriptProxy proxy = getScriptProxyForSessions();      if (proxy != null) {         proxy.addFunctionCall("newMessage",newMessage);      }   }} The Chatroom code is surprisingly simple. The chat content is stored in a Vector of Strings. The getChatContent()method just returns the chat content Vector to the browser. The postMessage()method is called when the user sends a new chat message. The method verifies whether the user is logged in, and adds the current time and username to the chat message and then appends the message to the chat content. The method also calls the postNewMessage() method that is used to show new chat content to all logged-in users. Note that the postMessage() method does not return any value. We let DWR and reverse AJAX functionality show the chat message to all users, including the user who sent the message. The getScriptProxyForSessions() and postNewMessage() methods use reverse AJAX to update the chat areas of all logged-in users with the new message. And that is it! The chat room sample is very straightforward and basic functionality is already in place, and the application is ready for further development. Testing the Chat We test the chat room application with three users: Smith, Brown, and Jones. We have given some screenshots of a typical scenario in a chat room here. Both Smith and Brown log into the system and exchange some messages. Both users see empty chat rooms when they log in and start chatting. The empty area that is above the send message input field is reserved for chat content. Smith and Brown exchange some messages as is seen in the following screenshot: The third user, Jones, joins the chat and sees all the previous messages in the chat room. Jones then exchanges some messages with Smith and Brown. Smith and Brown log out from the system leaving Jones alone in the chat room (until she also logs out). This is visible in the following screenshot: Summary This sample application showed how to use DWR in a chat room application. This application makes it clear that DWR makes development of these kind of collaborative applications very easy. DWR itself does not even play a big part in the applications. DWR is just a transparent feature of the application. So developers can concentrate on the actual project and aspects such as persistence of data and a neat user interface, instead of the low-level details of AJAX.    
Read more
  • 0
  • 0
  • 2726

article-image-roles-and-permissions-moodle-administration-part1
Packt
23 Oct 2009
1 min read
Save for later

Roles and Permissions in Moodle Administration: Part1

Packt
23 Oct 2009
1 min read
Lets get started. Moodle's PreDefined Roles Moodle comes with a number of predefined roles. These standard roles are suitable for some educational setups, but most institutions require modifications to the roles' system in order to tailor Moodle to their specific needs. Each role has permissions for a number of actions that can be carried out in Moodle. For example, an administrator and a course creator are able to create new courses, whereas all other roles are denied this right. Likewise, a teacher is allowed to moderate forums, whereas students are only allowed to contribute to them. Before we can actually do anything with roles, we need to understand the concept of contexts, which is dealt with next. Contexts Contexts are the areas in Moodle where roles can be assigned to users. A role can be assigned within different contexts. A user has a role in any given context, where a context can be a course, an activity module, a user, a block, or Moodle itself. Moodle comes with the following seven contexts that you will come across a lot in this article.
Read more
  • 0
  • 0
  • 3470
article-image-integrating-twitter-and-youtube-mediawiki
Packt
23 Oct 2009
5 min read
Save for later

Integrating Twitter and YouTube with MediaWiki

Packt
23 Oct 2009
5 min read
Twitter in MediaWiki Twitter (http://www.twitter.com) is a micro-blogging service that allows users to convey the world (or at least the small portion of it on Twitter) what they are doing, in messages of 140 characters or less. It is possible to embed these messages in external websites, which is what we will be doing for JazzMeet. We can use the updates to inform our wiki's visitors of the latest JazzMeet being held across the world, and they can send a response to the JazzMeet Twitter account. Shorter Links Because Twitter only allows posts of up to 140 characters, many Twitter usersmake use of URL-shortening services such as Tiny URL (http://tinyurl.com), and notlong (http://notlong.com) to turn long web addresses into short, more manageable URLs. Tiny URL assigns a random URL such as http://tinyurl.com/3ut9p4, while notlong allows you to pick a free sub-domain to redirect to your chosen address, such as http://asgkasdgadg.notlong.com. Twitter automatically shortens web addresses in your posts. Creating a Twitter Account Creating a Twitter account is quite easy. Just fill in the username, password, and email address fields, and submit the registration form, once you have read and accepted the terms and conditions. If your chosen username is free, your account is created instantly. Once your account has been created, you can change the settings such as your display name and your profile's background image, to help blur the distinction between your website and your Twitter profile. Colors can be specified as "hex" values under the Design tab of your Twitter account's settings section. The following color codes change the link colors to our JazzMeet's palette of browns and reds: As you can see in the screenshot, JazzMeet's Twitter profile now looks a little more like the JazzMeet wiki. By doing this, the visitors catching up with JazzMeet's events on Twitter will not be confused by a sudden change in color scheme:   Embedding Twitter Feeds in MediaWiki Twitter provides a few ways to embed your latest posts in to your own website(s); simply log in and go to http://www.twitter.com/badges. Flash: With this option you can show just your posts, or your posts and your friends' most recent posts on Twitter. HTML and JavaScript: You can configure the code to show between 1 and 20 of your most recent Twitter posts. As JazzMeet isn't really the sort of wiki the visitors would expect to find on Flash, we will be using the HTML and JavaScript version. You are provided with the necessary code to embed in your website or wiki. We will add it to the JazzMeet skin template, as we want it to be displayed on every page of our wiki, just beneath our sponsor links. Refer to the following code: <div id="twitter_div"><h2 class="twitter-title">Twitter Updates</h2><ul id="twitter_update_list"></ul></div><script type="text/javascript" src="http://twitter.com/javascripts/blogger.js"></script><script type="text/javascript" src="http://twitter.com/statuses/user_timeline/jazzmeet.json?callback=twitterCallback2&count=5"></script> The JavaScript given at the bottom of the code can be moved just above the </body> tag of your wiki's skin template. This will help your wiki to load other important elements of your wiki before the Twitter status. You will need to replace "jazzmeet" in the code with your own Twitter username, otherwise you will receive JazzMeet's Twitter updates, and not your own. It is important to leave the unordered list of ID twitter_update_list as it is, as this is the element the JavaScript code looks for to insert a list item containing each of your twitter messages in the page. Styling Twitter's HTML We need to style the Twitter HTML by adding some CSS to change the colors and style of the Twitter status code: #twitter_div {background: #FFF;border: 3px #BEB798 solid;color: #BEB798;margin: 0;padding: 5px;width: 165px;}#twitter_div a {color: #8D1425 !important;}ul#twitter_update_list {list-style-type: none;margin: 0;padding: 0;}#twitter_update_list li {color: #38230C;display: block;}h2.twitter-title {color: #BEB798;font-size: 100%;} There are only a few CSS IDs and classes that need to be taken care of. They are as follows: #twitter_div is the element that contains the Twitter feeds. #twitter_update_list is the ID applied to the unordered list. Styling this affects how your Twitter feeds are displayed. .twitter-title is the class applied to the Twitter feed's heading (which you can remove, if necessary). Our wiki's skin for JazzMeet now has JazzMeet's Twitter feed embedded in the righthand column, allowing visitors to keep up-to-date with the latest JazzMeet news. Inserting Twitter as Page Content Media Wiki does not allow JavaScript to be embedded in a page via the "edit" function, so you won't be able to insert a Twitter status feed directly in a page unless it is in the template itself. Even if you inserted the relevant JavaScript links into your MediaWiki skin template, they are relevant only for one Twitter profile ("jazzmeet", in our case).  
Read more
  • 0
  • 0
  • 8266

article-image-adonet-entity-framework
Packt
23 Oct 2009
6 min read
Save for later

ADO.NET Entity Framework

Packt
23 Oct 2009
6 min read
Creating an Entity Data Model You can create the ADO.NET Entity Data Model in one of the two ways: Use the ADO.NET Entity Data Model Designer Use the command line Entity Data Model Designer called EdmGen.exe We will first take a look at how we can design an Entity Data Model using the ADO.NET Entity Data Model Designer which is a Visual Studio wizard that is enabled after you install ADO.NET Entity Framework and its tools. It provides a graphical interface that you can use to generate an Entity Data Model. Creating the Payroll Entity Data Model using the ADO.NET Entity Data Model Designer Here are the tables of the 'Payroll' database that we will use to generate the data model: Employee Designation Department Salary ProvidentFund To create an entity data model using the ADO.NET Entity Data Model Designer, follow these simple steps: Open Visual Studio.NET and create a solution for a new web application project as seen below and save with a name. Switch to the Solution Explorer, right click and click on Add New Item as seen in the following screenshot: Next, select ADO.NET Entity Data Model from the list of the templates displayed as shown in the following screenshot:   Name the Entity Data Model PayrollModel and click on Add. Select Generate from database from the Entity Data Model Wizard as shown in the following screenshot: Note that you can also use the Empty model template to create the Entity Data Model yourself. If you select the Empty Data Model template and click on next, the following screen appears: As you can see from the above figure, you can use this template to create the Entity Data Model yourself. You can create the Entity Types and their relationships manually by dragging items from the toolbox. We will not use this template in our discussion here. So, let's get to the next step. Click on Next in the Entity Data Model Wizard window shown earlier. The modal dialog box will now appear and prompts you to choose your connection as shown in the following figure: Click on New Connection Now you will need to specify the connection properties and parameters as shown in the following figure: We will use a dot to specify the database server name. This implies that we will be using the database server of the localhost, which is the current system in use. After you specify the necessary user name, password, and the server name, you can test your connection using the Test Connection button. When you do so, the message Test connection succeeded gets displayed in the message box as shown in the previous figure. When you click on OK on the Test connection dialog box, the following screen appears: <connectionStrings> <add name="PayrollEntities" connectionString="metadata=res:// *; provider=System.Data.SqlClient;provider connection string=&quot; Data Source=.;Initial Catalog=Payroll;User ID=sa;Password=joydip1@3; MultipleActiveResultSets=True&quot;" providerName="System.Data.EntityClient" /> </connectionStrings> Note the Entity Connection String generated automatically. This connection string will be saved in the ConnectionStrings section of your application's web.config file. This is how it will look like: When you click on Next in the previous figure, the following screen appears: Expand the Tables node and specify the database objects that you require in the Entity Data Model to be generated as shown in the following figure: Click on Finish to generate the Entity Data Model. Here is the output displayed in the Output Window while the Entity Data Model is being generated: Your Entity Data Model has been generated and saved in a file named PayrollModel.edmx. We are done creating our first Entity Data Model using the ADO.NET Entity Data Model Designer tool. When you open the Payroll Entity Data Model that we just created in the designer view, it will appear as shown in the following figure: Note how the Entity Types in the above model are related to one another. These relationships have been generated automatically by the Entity Data Model Designer based on the relationships between the tables of the Payroll database. In the next section, we will learn how we can create an Entity Data Model using the EdmGen.exe command line tool. Creating the Payroll Data Model Using the EdmGen Tool We will now take a look at how to create a data model using the Entity Data Model generation tool called EdmGen. The EdmGen.exe command line tool can be used to do one or more of the following: Generate the .cdsl, .msl, and .ssdl files as part of the Entity Data Model Generate object classes from a .csdl file Validate an Entity Data Model The EdmGen.exe command line tool generates the Entity Data Model as a set of three files: .csdl, .msl, and .ssdl. If you have used the ADO.NET Entity Data Model Designer to generate your Entity Data Model, the .edmx file generated will contain the CSDL, MSL, and the SSDL sections. You will have a single .edmx file that bundles all of these sections into it. On the other hand, if you use the EdmGen.exe tool to generate the Entity Data Model, you would find three distinctly separate files with .csdl, .msl or .ssdl extensions. Here is a list of the major options of the EdmGen.exe command line tool: Option Description /help Use this option to display help on all the possible options of this tool. The short form is /? /language:CSharp Use this option to generate code using C# language /language:VB Use this option to generate code using VB language /provider:<string> Use this option to specify the name of the ADO.NET data provider that you would like to use. /connectionstring: <connection string> Use this option to specify the connection string to be used to connect to the database /namespace:<string> Use this option to specify the name of the namespace /mode:FullGeneration Use this option to generate your CSDL, MSL, and SSDL objects from the database schema /mode:EntityClassGeneration Use this option to generate your entity classes from a given CSDL file /mode:FromSsdlGeneration Use this option to generate MSL, CSDL, and Entity Classes from a given SSDL file /mode:ValidateArtifacts Use this option to validate the CSDL, SSDL, and MSL files /mode:ViewGeneration Use this option to generate mapping views from the CSDL, SSDL, and MSL files  
Read more
  • 0
  • 0
  • 3771

article-image-python-data-persistence-using-mysql
Packt
23 Oct 2009
8 min read
Save for later

Python Data Persistence using MySQL

Packt
23 Oct 2009
8 min read
To keep things simple though, the article doesn’t discuss how to implement database-backed web pages with Python, concentrating only on how to connect Python with MySQL. Sample Application The best way to learn new programming techniques is to write an application that exercises them. This article will walk you through the process of building a simple Python application that interacts with a MySQL database. In a nutshell, the application picks up some live data from a web site and then persists it to an underlying MySQL database. For the sake of simplicity, it doesn’t deal with a large dataset. Rather, it picks up a small subset of data, storing it as a few rows in the underlying database. In particular, the application gets the latest post from the Packt Book Feed page available at http://feeds.feedburner.com/packtpub/sDsa?format=xml. Then, it analyzes the post’s title, finding appropriate tags for the article associated with the post, and finally inserts information about the post into the posts and posttags underlying database tables. As you might guess, a single post may be associated with more than one tag, meaning a record in the posts table may be related to several records in the posttags table. Diagrammatically, the sample application components and their interactions might look like this: Note the use of appsample.py. This script file will contain all the application code written in Python. In particular, it will contain the list of tags, as well as several Python functions packaging application logic. Software Components To build the sample discussed in the article you’re going to need the following software components installed on your computer: Python 2.5.x MySQLdb 1.2.x MySQL 5.1 All these software components can be downloaded and used for free. Although you may already have these pieces of software installed on your computer, here’s a brief overview of where you can obtain them. You can download an appropriate Python release from the Downloads page at Python’s web site at http://python.org/download/. You may be tempted to download the most recent release. Before you choose the release, however, it is recommended that you visit the Python for MySQL page at http://sourceforge.net/projects/mysql-python/ to check what Python releases are supported by the current MySQLdb module that will be used to connect your Python installation with MySQL. MySQLdb is the Python DB API-2.0 interface for MySQL. You can pick up the latest MySQLdb package (version 1.2.2 at the time of writing) from the sourceforge.net’s Python for MySQL page at http://sourceforge.net/projects/mysql-python/. Before you can install it, though, make sure you have Python installed in your system. You can obtain the MySQL 5.1 distribution from the mysql.com web site at http://dev.mysql.com/downloads/mysql/5.1.html, picking up the package designed for your operating system. Setting up the Database Assuming you have all the software components that were outlined in the preceding section installed in your system, you can now start building the sample application. The first step is to create the posts and posttags tables in your underlying MySQL database. As mentioned earlier, a single post may be associated with more than one tag. What this means in practice is that the posts and posttags tables should have a foreign key relationship. In particular, you might create these tables as follows: CREATE TABLE posts ( title VARCHAR(256) PRIMARY KEY, guid VARCHAR(1000), pubDate VARCHAR(50) ) ENGINE = InnoDB; CREATE TABLE posttags ( title VARCHAR(256), tag VARCHAR(20), PRIMARY KEY(title,tag), FOREIGN KEY(title) REFERENCES posts(title) ) ENGINE = InnoDB; As you might guess, you don’t need to populate above tables with data now. This will be automatically done later when you launch the sample. Developing the Script Now that you have the underlying database ready, you can move on and develop the Python code to complete the sample. In particular, you’re going to need to write the following components in Python: tags nested list of tags that will be used to describe the posts obtained from the Packt Book Feed page. obtainPost function that will be used to obtain the information about the latest post from the Packt Book Feed page. determineTags function that will determine appropriate tags to be applied to the latest post obtained from the Packt Book Feed page. insertPost function that will insert the information about the post obtained into the underlying database tables: posts and posttags. execPr function that will make calls to the other, described above functions. You will call this function to launch the application. All the above components will reside in a single file, say, appsample.py that you can create in your favorite text editor, such as vi or Notepad. First, add the following import declarations to appsample.py: import MySQLdb import urllib2 import xml.dom.minidom As you might guess, the first module is required to connect Python with MySQL, providing the Python DB API-2.0 interface for MySQL. The other two are needed to obtain and then parse the Packt Book Feed page’s data. You will see them in action in the obtainPost function in a moment. But first let’s create a nested list of tags that will be used by the determineTags function that determines the tags appropriate for the post being analyzed. To save space here, the following list contains just a few tags. You may and should include more tags to this list, of course. tags=["Python","Java","Drupal","MySQL","Oracle","Open Source"] The next step is to add the obtainPost function responsible for getting the data from the Packt Book Feed page and generating the post dictionary that will be utilized in further processing: def obtainPost(): addr = "http://feeds.feedburner.com/packtpub/sDsa?format=xml" xmldoc = xml.dom.minidom.parseString(urllib2.urlopen(addr).read()) item = xmldoc.getElementsByTagName("item")[0] title = item.getElementsByTagName("title")[0].firstChild.data guid = item.getElementsByTagName("guid")[0].firstChild.data pubDate = item.getElementsByTagName("pubDate")[0].firstChild.data post ={"title": title, "guid": guid, "pubDate": pubDate} return post Now that you have obtained all the required information about the latest post on the Packt Book Feed page, you can analyze the post’s title to determine appropriate tags. For that, add the determineTags function to appsample.py: def determineTags(title, tagslist): curtags=[] for curtag in tagslist: if title.find(curtag)>-1:curtags.append(curtag) return curtags By now, you have both the post and tags to be persisted to the database. So, add the insertPost function that will handle this task (don’t forget to change the parameters specified to the MySQLdb.connect function for the actual ones): def insertPost(title, guid, pubDate, curtags): db=MySQLdb.connect(host="localhost",user="usrsample",passwd="pswd",db="dbsample") c=db.cursor() c.execute("""INSERT INTO posts (title, guid, pubDate) VALUES(%s, %s,%s)""", (title, guid, pubDate)) db.commit() for tag in curtags: c.execute("""INSERT INTO posttags (title, tag) VALUES(%s,%s)""", (title, tag)) db.commit() db.close() All that is left to do is add the execPr function that brings all the pieces together, calling the above functions in the proper order: def execPr(): p = obtainPost() t = determineTags(p["title"],tags) insertPost(p["title"], p["guid"], p["pubDate"], t) Now let’s test the code we just wrote. The simplest way to do this is through Python’s interactive command line. To start an interactive Python session, you can type python at your system shell prompt. It’s important to realize that since the sample discussed here is going to obtain some data from the web, you must connect to the Internet before you launch the application. Once you’re connected, you can launch the execPr function in your Python session, as follows: >>>import appsample >>>appsample.execPr() If everything is okay, you should see no messages. To make sure that everything really went as planned, you can check the posts and posttags tables. To do this, you might connect to the database with the MySQL command-line tool and then issue the following SQL commands: SELECT * FROM posts; The above should generate the output that might look like this: |title |guid |pubDate ------------------------------------------------------------------ Open Source CMS Award Voting Now Closed | http://www.packtpub.com/ article/2008-award-voting-closed | Tue, 21 Oct 2008 09:29:54 +0100 Then, you might want to check out the posttags table: SELECT * FROM posttags; This might generate the following output: |title |tag Open Source CMS Award Voting Now Closed | Open Source Please note that you may see different results since you are working with live data. Another thing to note here is that if you want to re-run the sample, you first need to empty the posts and posttags tables. Otherwise, you will encounter the problem related to the primary key constraints. However, that won’t be a problem at all if you re-run the sample in a few days, when a new post or posts appear on the Packt Book Feed page. Conclusion In this article you looked at a simple Python application persisting data to an underlying MySQL database. Although, for the sake of simplicity, the sample discussed here doesn’t offer a web interface, it illustrates how you can obtain data from the Internet, and then utilize it within your application, and finally store that data in the database.
Read more
  • 0
  • 0
  • 8403
article-image-writing-package-python
Packt
23 Oct 2009
18 min read
Save for later

Writing a Package in Python

Packt
23 Oct 2009
18 min read
Writing a Package Its intents are: To shorten the time needed to set up everything before starting the real work, in other words the boiler-plate code To provide a standardized way to write packages To ease the use of a test-driven development approach To facilitate the releasing process It is organized in the following four parts: A common pattern for all packages that describes the similarities between all Python packages, and how distutils and setuptools play a central role How generative programming (http://en.wikipedia.org/wiki/Generative_programming) can help this through the template-based approach The package template creation, where everything needed to work is set Setting up a development cycle A Common Pattern for All Packages The easiest way to organize the code of an application is to split it into several packages using eggs. This makes the code simpler, and easier to understand, maintain, and change. It also maximizes the reusability of each package. They act like components. Applications for a given company can have a set of eggs glued together with a master egg. Therefore, all packages can be built using egg structures. This section presents how a namespaced package is organized, released, and distributed to the world through distutils and setuptools. Writing an egg is done by layering the code in a nested folder that provides a common prefix namespace. For instance, for the Acme company, the common namespace can be acme. The result is a namespaced package. For example, a package whose code relates to SQL can be called acme.sql. The best way to work with such a package is to create an acme.sql folder that contains the acme and then the sql folder: setup.py, the Script That Controls Everything The root folder contains a setup.py script, which defines all metadata as described in the distutils module, combined as arguments in a call to the standard setup function. This function was extended by the third-party library setuptools that provides most of the egg infrastructure. The boundary between distutils and setuptools is getting fuzzy, and they might merge one day. Therefore, the minimum content for this file is: from setuptools import setupsetup(name='acme.sql') name gives the full name of the egg. From there, the script provides several commands that can be listed with the -help-commands option. $ python setup.py --help-commands Standard commands:  build             build everything needed to install  ...  install           install everything from build directory  sdist         create a source distribution  register      register the distribution  bdist         create a built (binary) distributionExtra commands:  develop       install package in 'development mode'  ...  test          run unit tests after in-place build alias         define a shortcut  bdist_egg     create an "egg" distribution The most important commands are the ones left in the preceding listing. Standard commands are the built-in commands provided by distutils, whereas Extra commands are the ones created by third-party packages such as setuptools or any other package that defines and registers a new command. sdist The sdist command is the simplest command available. It creates a release tree where everything needed to run the package is copied. This tree is then archived in one or many archived files (often, it just creates one tar ball). The archive is basically a copy of the source tree. This command is the easiest way to distribute a package from the target system independently. It creates a dist folder with the archives in it that can be distributed. To be able to use it, an extra argument has to be passed to setup to provide a version number. If you don't give it a version value, it will use version = 0.0.0: from setuptools import setupsetup(name='acme.sql', version='0.1.1' This number is useful to upgrade an installation. Every time a package is released, the number is raised so that the target system knows it has changed. Let's run the sdist command with this extra argument: $ python setup.py sdistrunning sdist...creating disttar -cf dist/acme.sql-0.1.1.tar acme.sql-0.1.1gzip -f9 dist/acme.sql-0.1.1.tarremoving 'acme.sql-0.1.1' (and everything under it)$ ls dist/acme.sql-0.1.1.tar.gz Under Windows, the archive will be a ZIP file. The version is used to mark the name of the archive, which can be distributed and installed on any system having Python. In the sdist distribution, if the package contains C libraries or extensions, the target system is responsible for compiling them. This is very common for Linux-based systems or Mac OS because they commonly provide a compiler. But it is less usual to have it under Windows. That's why a package should always be distributed with a pre-built distribution as well, when it is intended to run under several platforms. The MANIFEST.in File When building a distribution with sdist, distutils browse the package directory looking for files to include in the archive. distutils will include: All Python source files implied by the py_modules, packages, and scripts option All C source files listed in the ext_modules option Files that match the glob pattern test/test*.py README, README.txt, setup.py, and setup.cfg files Besides, if your package is under Subversion or CVS, sdist will browse folders such as .svn to look for files to include .sdist builds a MANIFEST file that lists all files and includes them into the archive. Let's say you are not using these version control systems, and need to include more files. Now, you can define a template called MANIFEST.in in the same directory as that of setup.py for the MANIFEST file, where you indicate to sdist which files to include. This template defines one inclusion or exclusion rule per line, for example: include HISTORY.txtinclude README.txtinclude CHANGES.txtinclude CONTRIBUTORS.txtinclude LICENSErecursive-include *.txt *.py The full list of commands is available at http://docs.python.org/dist/sdist-cmd.html#sdist-cmd. build and bdist To be able to distribute a pre-built distribution, distutils provide the build command, which compiles the package in four steps: build_py: Builds pure Python modules by byte-compiling them and copying them into the build folder. build_clib: Builds C libraries, when the package contains any, using Python compiler and creating a static library in the build folder. build_ext: Builds C extensions and puts the result in the build folder like build_clib. build_scripts: Builds the modules that are marked as scripts. It also changes the interpreter path when the first line was set (!#) and fixes the file mode so that it is executable. Each of these steps is a command that can be called independently. The result of the compilation process is a build folder that contains everything needed for the package to be installed. There's no cross-compiler option yet in the distutils package. This means that the result of the command is always specific to the system it was build on. Some people have recently proposed patches in the Python tracker to make distutils able to cross-compile the C parts. So this feature might be available in the future. When some C extensions have to be created, the build process uses the system compiler and the Python header file (Python.h). This include file is available from the time Python was built from the sources. For a packaged distribution, an extra package called python-dev often contains it, and has to be installed as well. The C compiler used is the system compiler. For Linux-based system or Mac OS X, this would be gcc. For Windows, Microsoft Visual C++ can be used (there's a free command-line version available) and the open-source project MinGW as well. This can be configured in distutils. The build command is used by the bdist command to build a binary distribution. It calls build and all dependent commands, and then creates an archive in the same was as sdist does. Let's create a binary distribution for acme.sql under Mac OS X: $ python setup.py bdistrunning bdistrunning bdist_dumbrunning build...running install_scriptstar -cf dist/acme.sql-0.1.1.macosx-10.3-fat.tar .gzip -f9 acme.sql-0.1.1.macosx-10.3-fat.tarremoving 'build/bdist.macosx-10.3-fat/dumb' (and everything under it)$ ls dist/acme.sql-0.1.1.macosx-10.3-fat.tar.gz    acme.sql-0.1.1.tar.gz Notice that the newly created archive's name contains the name of the system and the distribution it was built under (Mac OS X 10.3). The same command called under Windows will create a specific distribution archive: C:acme.sql> python.exe setup.py bdist...C:acme.sql> dir dist25/02/2008  08:18     <DIR>          .25/02/2008  08:18     <DIR>          ..25/02/2008  08:24             16 055 acme.sql-0.1.win32.zip               1 File(s)          16 055 bytes               2 Dir(s)   22 239 752 192 bytes free If a package contains C code, apart from a source distribution, it's important to release as many different binary distributions as possible. At the very least, a Windows binary distribution is important for those who don't have a C compiler installed. A binary release contains a tree that can be copied directly into the Python tree. It mainly contains a folder that is copied into Python's site-packages folder. bdist_egg The bdist_egg command is an extra command provided by setuptools. It basically creates a binary distribution like bdist, but with a tree comparable to the one found in the source distribution. In other words, the archive can be downloaded, uncompressed, and used as it is by adding the folder to the Python search path (sys.path). These days, this distribution mode should be used instead of the bdist-generated one. install The install command installs the package into Python. It will try to build the package if no previous build was made and then inject the result into the Python tree. When a source distribution is provided, it can be uncompressed in a temporary folder and then installed with this command. The install command will also install dependencies that are defined in the install_requires metadata. This is done by looking at the packages in the Python Package Index (PyPI). For instance, to install pysqlite and SQLAlchemy together with acme.sql, the setup call can be changed to: from setuptools import setupsetup(name='acme.sql', version='0.1.1',      install_requires=['pysqlite', 'SQLAlchemy']) When we run the command, both dependencies will be installed. How to Uninstall a Package The command to uninstall a previously installed package is missing in setup.py. This feature was proposed earlier too. This is not trivial at all because an installer might change files that are used by other elements of the system. The best way would be to create a snapshot of all elements that are being changed, and a record of all files and directories created. A record option exists in install to record all files that have been created in a text file: $ python setup.py install --record installation.txtrunning install...writing list of installed files to 'installation.txt' This will not create any backup on any existing file, so removing the file mentioned might break the system. There are platform-specific solutions to deal with this. For example, distutils allow you to distribute the package as an RPM package. But there's no universal way to handle it as yet. The simplest way to remove a package at this time is to erase the files created, and then remove any reference in the easy-install.pth file that is located in the sitepackages folder. develop setuptools added a useful command to work with the package. The develop command builds and installs the package in place, and then adds a simple link into the Python site-packages folder. This allows the user to work with a local copy of the code, even though it's available within Python's site-packages folder. All packages that are being created are linked with the develop command to the interpreter. When a package is installed this way, it can be removed specifically with the -u option, unlike the regular install: $ sudo python setup.py developrunning develop...Adding iw.recipe.fss 0.1.3dev-r7606 to easy-install.pth fileInstalled /Users/repos/ingeniweb.sourceforge.net/iw.recipe.fss/trunkProcessing dependencies ...$ sudo python setup.py develop -urunning developRemoving...Removing iw.recipe.fss 0.1.3dev-r7606 from easy-install.pth file Notice that a package installed with develop will always prevail over other versions of the same package installed. test Another useful command is test. It provides a way to run all tests contained in the package. It scans the folder and aggregates the test suites it finds. The test runner tries to collect tests in the package but is quite limited. A good practice is to hook an extended test runner such as zope.testing or Nose that provides more options. To hook Nose transparently to the test command, the test_suite metadata can be set to 'nose.collector' and Nose added in the test_requires list: setup(...test_suite='nose.collector',test_requires=['Nose'],...) register and upload To distribute a package to the world, two commands are available: register: This will upload all metadata to a server. upload: This will upload to the server all archives previously built in the dist folder. The main PyPI server, previously named the Cheeseshop, is located at http://pypi.python.org/pypi and contains over 3000 packages from the community. It is a default server used by the distutils package, and an initial call to the register command will generate a .pypirc file in your home directory. Since the PyPI server authenticates people, when changes are made to a package, you will be asked to create a user over there. This can also be done at the prompt: $ python setup.py registerrunning register...We need to know who you are, so please choose either: 1. use your existing login, 2. register as a new user, 3. have the server generate a new password for you (and email it toyou), or 4. quitYour selection [default 1]: Now, a .pypirc file will appear in your home directory containing the user and password you have entered. These will be used every time register or upload is called: [server-index]username: tarekpassword: secret There is a bug on Windows with Python 2.4 and 2.5. The home directory is not found by distutils unless a HOME environment variable is added. But, this has been fixed in 2.6. To add it, use the technique where we modify the PATH variable. Then add a HOME variable for your user that points to the directory returned by os.path.expanduser('~'). When the download_url metadata or the url is specified, and is a valid URL, the PyPI server will make it available to the users on the project web page as well. Using the upload command will make the archive directly available at PyPI, so the download_url can be omitted: Distutils defines a Trove categorization (see PEP 301: http://www.python.org/dev/peps/pep-0301/#distutils-trove-classification) to classify the packages, such as the one defined at Sourceforge. The trove is a static list that can be found at http://pypi.python.org/pypi?%3Aaction=list_classifiers, and that is augmented from time to time with a new entry. Each line is composed of levels separated by "::": ...Topic :: TerminalsTopic :: Terminals :: SerialTopic :: Terminals :: TelnetTopic :: Terminals :: Terminal Emulators/X TerminalsTopic :: Text Editors Topic :: Text Editors :: DocumentationTopic :: Text Editors :: Emacs... A package can be classified in several categories, which can be listed in the classifiers meta-data. A GPL package that deals with low-level Python code (for instance) can use: Programming Language :: PythonTopic :: Software Development :: Libraries :: Python ModulesLicense :: OSI Approved :: GNU General Public License (GPL) Python 2.6 .pypirc Format The .pypirc file has evolved under Python 2.6, so several users and their passwords can be managed along with several PyPI-like servers. A Python 2.6 configuration file will look somewhat like this: [distutils]index-servers =    pypi    alternative-server    alternative-account-on-pypi[pypi]username:tarekpassword:secret[alternative-server]username:tarekpassword:secretrepository:http://example.com/pypi The register and upload commands can pick a server with the help of the -r option, using the repository full URL or the section name: # upload to http://example.com/pypi$ python setup.py sdist upload -r   alternative-server#  registers with default account (tarek at pypi)$ python setup.py register#  registers to http://example.com$ python setup.py register -r http://example.com/pypi This feature allows interaction with servers other than PyPI. When dealing with a lot of packages that are not to be published at PyPI, a good practice is to run your own PyPI-like server. The Plone Software Center (see http://plone.org/products/plonesoftwarecenter) can be used, for example, to deploy a web server that can interact with distutils upload and register commands. Creating a New Command distutils allows you to create new commands, as described in http://docs.python.org/dist/node84.html. A new command can be registered with an entry point, which was introduced by setuptools as a simple way to define packages as plug-ins. An entry point is a named link to a class or a function that is made available through some APIs in setuptools. Any application can scan for all registered packages and use the linked code as a plug-in. To link the new command, the entry_points metadata can be used in the setup call: setup(name="my.command",          entry_points="""             [distutils.commands]             my_command = my.command.module.Class          """) All named links are gathered in named sections. When distutils is loaded, it scans for links that were registered under distutils.commands. This mechanism is used by numerous Python applications that provide extensibility. setup.py Usage Summary There are three main actions to take with setup.py: Build a package. Install it, possibly in develop mode. Register and upload it to PyPI. Since all the commands can be combined in the same call, some typical usage patterns are: # register the package with PyPI, creates a source and# an egg distribution, then upload them$ python setup.py register sdist bdist_egg upload# installs it in-place, for development purpose$ python setup.py develop# installs it$ python setup.py install The alias Command To make the command line work easily, a new command has been introduced by setuptools called alias. In a file called setup.cfg, it creates an alias for a given combination of commands. For instance, a release command can be created to perform all actions needed to upload a source and a binary distribution to PyPI: $ python setup.py alias release register sdist bdist_egg uploadrunning aliasWriting setup.cfg$ python setup.py release... Other Important Metadata Besides the name and the version of the package being distributed, the most important arguments setup can receive are: description: A few sentences to describe the package long_description: A full description that can be in reStructuredText keywords: A list of keywords that define the package author: The author's name or organization author_email: The contact email address url: The URL of the project license: The license (GPL, LGPL, and so on) packages: A list of all names in the package; setuptools provides a small function called find_packages that calculates this namespace_packages: A list of namespaced packages A completed setup.py file for acme.sql would be: import osfrom setuptools import setup, find_packagesversion = '0.1.0'README = os.path.join(os.path.dirname(__file__), 'README.txt')long_description = open(README).read() + 'nn'setup(name='acme.sql',      version=version,      description=("A package that deals with SQL, "                    "from ACME inc"),      long_description=long_description,      classifiers=[        "Programming Language :: Python",        ("Topic :: Software Development :: Libraries ::          "Python Modules"),        ],      keywords='acme sql',      author='Tarek',      author_email='tarek@ziade.org',      url='http://ziade.org',      license='GPL',      packages=find_packages(),      namespace_packages=['acme'],      install_requires=['pysqlite','SQLAchemy']      ) The two comprehensive guides to keep under your pillow are: The distutils guide at http://docs.python.org/dist/dist.html The setuptools guide at http://peak.telecommunity.com/DevCenter/setuptools 
Read more
  • 0
  • 0
  • 5460

article-image-comparing-cursor-and-set-approaches-processing-relational-data
Packt
23 Oct 2009
5 min read
Save for later

Comparing Cursor and Set Approaches in Processing Relational Data

Packt
23 Oct 2009
5 min read
To give you an idea about cursor, the following DECLARE statement creates a cursor (named zero_bal_crs), which gives you access to the rows of a payment table, rows that have 100 or less balances. DECLARE zero_bal_crs CURSOR FOR SELECT * FROM payment WHERE payment_bal < 100 You then fetch and process each of the rows sequentially; the process can be, for example, summing up rows by product group and loading the sums into a summary table. You develop the process outside of the cursor, for example, in a stored procedure. (You’ll see in the examples that the cursor-based process is procedural). Instead of processing row-by-row sequentially, you can process relational data set-by-set, without a cursor. For example, to sum all payment rows with 100 or less balances and load it into a table (named payment_100orless_bal in the following SQL statement), you can use the following SQL statement. This single SQL statement completely processes the rows that meet the condition, all at once, as a set. INSERT INTO payment_100orless_bal SELECT SUM(payment_bal) FROM payment WHERE payment_bal < 100 The following three examples, which are among those I most frequently encountered, further compare cursor-based processing with set processing. Example 1: Sequential Loop cf. One SQL Statement Our process in the first example is to summarize sales transactions by product. Listing 1 shows the DDLs for creating the sales transaction table and sales product table that stores the summary. Listing 1.1: DDLs of the Example 1 Tables CREATE TABLE sales_transactions( product_code INT, sales_amount DEC(8,2), discount INT);CREATE TABLE sales_product(product_code INT, sales_amount DEC(8,2)); Listing 1.2 shows a cursor-based stored procedure that implements the process. In this example, what the cursor (sales_crs) does is simply putting all rows in ascending order by their product codes. As you might have expected, this stored procedure applies loop with if-then-else programming construct to process the data row-by-row. Listing 1.2 Cursor Procedural solution DELIMITER $$DROP PROCEDURE IF EXISTS ex1_cursor $$USE sales $$CREATE PROCEDURE ex1_cursor()BEGINDECLARE p INT DEFAULT 0;DECLARE s DECIMAL(8,2) DEFAULT 0;DECLARE d INT DEFAULT 0;DECLARE done INT DEFAULT 0;DECLARE first_row INT DEFAULT 0;DECLARE px INT DEFAULT 0;DECLARE sx DECIMAL(8,2) DEFAULT 0;DECLARE sales_crs CURSOR FOR SELECT * FROM sales_transactions ORDER BY product_code;DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;OPEN sales_crs; REPEAT FETCH sales_crs INTO p, s, d;IF first_row = 0 THEN SET first_row = 1; SET px = p; SET sx = s * (100 - d)/100; ELSEIF NOT done THEN IF p <> px THEN INSERT INTO sales_product VALUES(px, sx); SET sx = s * (100 - d)/100; SET px = p; ELSE SET sx = sx + s * (100 - d)/100; END IF; ELSE INSERT INTO sales_product VALUES(px, sx); END IF; UNTIL done END REPEAT;CLOSE sales_crs;END $$DELIMITER ; The stored procedure in Listing 1.3 implements a set, processing to accomplish the same purpose as its foregoing cursor-based processing. You can see that this stored procedure is simpler than its cursor-based counterpart. Listing 1.3 Set Operation solution DELIMITER //USE sales //DROP PROCEDURE IF EXISTS ex1_sql //CREATE PROCEDURE ex1_sql ()BEGIN INSERT INTO sales_product SELECT product_code , SUM(sales_amount * (100 - discount)/100) FROM sales_transactions GROUP BY product_code;END //DELIMITER ; Example 2: Nested Cursor cf. Join Our 2nd example is to consolidate order by customer. While example 1 has one table, example 2 has two: order and item. The DDLs of the two tables are shown in Listing 2. To process two tables, our cursor implementation uses a nested loop (Listing 2.2) which makes it even more complex than example 1. Listing 2.1: DDL’s of the Example 2 Tables CREATE TABLE order ( , order_number INT , order_date DATE , customer_number INT);CREATE TABLE item ( , order_number INT , product_code INT , quantity INT , unit_price DEC(8,2)); Listing 2.2: Nested Cursor DELIMITER $$DROP PROCEDURE IF EXISTS ex2_cursor $$CREATE PROCEDURE ex2_cursor()BEGINDECLARE so INT;DECLARE sc INT;DECLARE io INT;DECLARE iq INT;DECLARE iu DEC(10,2);DECLARE done1 VARCHAR(5) DEFAULT 'START' ;DECLARE done2 VARCHAR(5) DEFAULT 'START' ;DECLARE sales_crs CURSOR FOR SELECT customer_number, order_number FROM sales_order ORDER BY customer_number, order_number;DECLARE order_crs CURSOR FOR SELECT order_number, quantity, unit_price FROM item WHERE order_number = so ORDER BY order_number;DECLARE CONTINUE HANDLER FOR NOT FOUND SET done1 = 'END';OPEN sales_crs; WHILE done1 <> 'END' DO FETCH sales_crs INTO sc, so; IF done1 <> 'END' THEN/* inner cursor */ OPEN oorder_crs; SET done2 = done1; WHILE done1 <> 'END' DO FETCH order_crs INTO io, iq, iu; IF done1 <> 'END' THEN INSERT INTO customer_order VALUES (sc, iq * iu); END IF;END WHILE; CLOSE order_crs; SET done1 = done2; END IF; END WHILE;CLOSE sales_crs;END $$DELIMITER ; Listing 2.3 is the set solution. We apply join in this 2nd example which makes it just a single SQL statement. You can see that this set stored procedure is again simpler than its cursor-based equivalent.    
Read more
  • 0
  • 0
  • 5580
Modal Close icon
Modal Close icon