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

7018 Articles
Packt
16 Dec 2010
15 min read
Save for later

Page Management – Part Two in CMS

Packt
16 Dec 2010
15 min read
  CMS Design Using PHP and jQuery Build and improve your in-house PHP CMS by enhancing it with jQuery Create a completely functional and a professional looking CMS Add a modular architecture to your CMS and create template-driven web designs Use jQuery plugins to enhance the "feel" of your CMS A step-by-step explanatory tutorial to get your hands dirty in building your own CMS         Read more about this book       (For more resources on this subject, see here.) Dates Dates are annoying. The scheme I prefer is to enter dates the same way MySQL accepts them—yyyy-mm-dd hh:mm:ss. From left to right, each subsequent element is smaller than the previous. It's logical, and can be sorted sensibly using a simple numeric sorter. Unfortunately, most people don't read or write dates in that format. They'd prefer something like 08/07/06. Dates in that format do not make sense. Is it the 8th day of the 7th month of 2006, or the 7th day of the 8th month of 2006, or even the 6th day of the 7th month of 2008? Date formats are different all around the world. Therefore, you cannot trust human administrators to enter the dates manually. A very quick solution is to use the jQuery UI's datepicker plugin. Temporarily (we'll remove it in a minute) add the highlighted lines to /ww.admin/pages/pages.js: other_GET_params:currentpageid }); $('.date-human').datepicker({ 'dateFormat':'yy-mm-dd' });}); When the date field is clicked, this appears: It's a great calendar, but there's still a flaw: Before you click on the date field, and even after you select the date, the field is still in yyyy-mm-dd format. While MySQL will thank you for entering the date in a sane format, you will have people asking you why the date is not shown in a humanly readable way. We can't simply change the date format to accept something more reasonable such as "May 23rd, 2010", because we would then need to ensure that we can understand this on the server-side, which might take more work than we really want to do. So we need to do something else. The datepicker plugin has an option which lets you update two fields at the same time. This is the solution—we will display a dummy field which is humanly readable, and when that's clicked, the calendar will appear and you will be able to choose a date, which will then be set in the human-readable field and in the real form field. Don't forget to remove that temporary code from /ww.admin/pages/pages.js. Because this is a very useful feature, which we will use throughout the admin area whenever a date is needed, we will add a global JavaScript file which will run on all pages. Edit /ww.admin/header.php and add the following highlighted line: <script src="/j/jquery.remoteselectoptions /jquery.remoteselectoptions.js"></script><script src="/ww.admin/j/admin.js"></script><link rel="stylesheet" href="http://ajax.googleapis.com /ajax/libs/jqueryui/1.8.0/themes/south-street /jquery-ui.css" type="text/css" /> And then we'll create the /ww.admin/j/ directory and a file named /ww.admin/j/admin.js: function convert_date_to_human_readable(){ var $this=$(this); var id='date-input-'+Math.random().toString() .replace(/\./,''); var dparts=$this.val().split(/-/); $this .datepicker({ dateFormat:'yy-mm-dd', modal:true, altField:'#'+id, altFormat:'DD, d MM, yy', onSelect:function(dateText,inst){ this.value=dateText; } }); var $wrapper=$this.wrap( '<div style="position:relative" />'); var $input=$('<input id="'+id+'" class="date-human-readable" value="'+date_m2h($this.val())+'" />'); $input.insertAfter($this); $this.css({ 'position':'absolute', 'opacity':0 }); $this .datepicker( 'setDate', new Date(dparts[0],dparts[1]-1,dparts[2]) );}$(function(){ $('input.date-human').each(convert_date_to_human_readable);}); This takes the computer-readable date input and creates a copy of it, but in a human-readable format. The original date input box is then made invisible and laid across the new one. When it is clicked, the date is updated on both of them, but only the human-readable one is shown. Much better. Easy for a human to read, and also usable by the server. Saving the page We created the form, and except for making the body textarea more user-friendly, it's just about finished. Let's do that now. When you click on the Insert Page Details button (or Update Page Details, if an ID was provided), the form data is posted to the server. We need to perform these actions before the page menu is displayed, so it is up-to-date. Edit /ww.admin/pages.php, and add the following highlighted lines before the load menu section: echo '<h1>Pages</h1>';// { perform any actionsif(isset($_REQUEST['action'])){ if($_REQUEST['action']=='Update Page Details' || $_REQUEST['action']=='Insert Page Details'){ require 'pages/action.edit.php'; } else if($_REQUEST['action']=='delete'){ 'pages/action.delete.php'; }}// }// { load menu If an action parameter is sent to the server, then the server will use this block to decide whether you want to edit or delete the page. We'll handle deletes later in the article. Notice that we are handling inserts and updates with the same file, action.edit.php—in the database, there is almost no difference between the two when using MySQL. So, let's create that file now. We'll do it a bit at a time, like how we did the form, as it's a bit long. Create /ww.admin/pages/action.edit.php with this code: <?phpfunction pages_setup_name($id,$pid){ $name=trim($_REQUEST['name']); if(dbOne('select id from pages where name="'.addslashes($name).'" and parent='.$pid.' and id!='.$id,'id')){ $i=2; while(dbOne('select id from pages where name="'.addslashes($name.$i).'" and parent='.$pid.' and id!='.$id,'id'))$i++; echo '<em>A page named "'.htmlspecialchars($name).'" already exists. Page name amended to "' .htmlspecialchars($name.$i).'".</em>'; $name=$name.$i; } return $name;} The first piece is a function which tests the submitted page name. If that name is the same as another page which has the same parent, then a number is added to the end and a message is shown explaining this. Here's an example, creating a page named "Home" in the top level (we already have a page named "Home"): Next we'll create a function for testing the inputted special variable. Add this to the same file: function pages_setup_specials($id=0){ $special=0; $specials=isset($_REQUEST['special']) ?$_REQUEST['special']:array(); foreach($specials as $a=>$b) $special+=pow(2,$a); $homes=dbOne("SELECT COUNT(id) AS ids FROM pages WHERE (special&1) AND id!=$id",'ids'); if($special&1){ // there can be only one homepage if($homes!=0){ dbQuery("UPDATE pages SET special=special-1 WHERE special&1"); } } else{ if($homes==0){ $special+=1; echo '<em>This page has been marked as the site\'s Home Page, because there must always be one.</em>'; } } return $special;} In this function, we build up the special variable, which is a bit field. A bit field is a number which uses binary math to combine a few "yes/no" answers into one single value. It's good for saving space and fields in the database.Each value has a value assigned to it which is a power of two. The interesting thing to note about powers of two is that in binary, they're always represented as a 1 with some 0s after it. For example, 1 is represented as 00000001, 2 is 00000010, 4 is 00000100, and so on.When you have a bit field such as 00000011 (each number here is a bit), it's easy to see that this is composed of the values 1 and 2 combined, which are 20 and 21 respectively.The & operator lets us check quickly if a certain bit is turned on (is 1) or not. For example, 00010011 & 16 is true, and 00010011 & 32 is false, because the 16 bit is on and the 32 bit is off. In the database, we set a bit for the homepage, which we say has a value of 1. In the previous function, we need to make sure that after inserting or updating a page, there is always exactly one homepage. The only other one we've set so far is "does not appear in navigation menu", which we've given the value 2. If we added a third bit flag ("is a 404 handler", for example), it would have the value 4, then 8, and so on. Okay—now we will set up our variables. Add this to the same file: // { set up common variables$id =(int)$_REQUEST['id'];$pid =(int)$_REQUEST['parent'];$keywords =$_REQUEST['keywords'];$description =$_REQUEST['description'];$associated_date =$_REQUEST['associated_date'];$title =$_REQUEST['title'];$name =pages_setup_name($id,$pid);$body =$_REQUEST['body'];$special =pages_setup_specials($id);if(isset($_REQUEST['page_vars'])) $vars =json_encode($_REQUEST['page_vars']);else $vars='[]';// } Then we will add the main body of the page update SQL to the same file: // { create SQL$q='edate=now(),type="'.addslashes($_REQUEST['type']).'", associated_date="'.addslashes($associated_date).'", keywords="'.addslashes($keywords).'", description="'.addslashes($description).'", name="'.addslashes($name).'", title="'.addslashes($title).'", body="'.addslashes($body).'",parent='.$pid.', special='.$special.',vars="'.addslashes($vars).'"';// } This is SQL which is common to both creating and updating a page. Finally we run the actual query and perform the action. Add this to the same file: // { run the queryif($_REQUEST['action']=='Update Page Details'){ $q="update pages set $q where id=$id"; dbQuery($q);}else{ $q="insert into pages set cdate=now(),$q"; dbQuery($q); $_REQUEST['id']=dbLastInsertId();}// }echo '<em>Page Saved</em>'; In the first case, we simply run an update. In the second, we run an insert, adding the creation date to the query, and then setting $_REQUEST['id'] to the ID of the entry that we just created. Creating new top-level pages If you've been trying all this, you'll have noticed that you can create a top-level page simply by clicking on the admin area's Pages link in the top menu, and then you're shown an empty Insert Page Details form. If you've been trying all this, you'll have noticed that you can create a top-level page simply by clicking on the admin area's Pages link in the top menu, and then you're shown an empty Insert Page Details form. It makes sense, though, to also have it available from the pagelist on the left-hand side. So, let's make that add main page button useful. If you remember, we created a pages_add_main_page function in the menu.js file, just as a placeholder until we got everything else done. Open up that file now, /ww.admin/pages/menu.js, and replace that function with the following two new functions: function pages_add_main_page(){ pages_new(0);}function pages_new(p){ $('<form id="newpage_dialog" action="/ww.admin/pages.php" method="post"> <input type="hidden" name="action" value="Insert Page Details" /> <input type="hidden" name="special[1]" value="1" /> <input type="hidden" name="parent" value="'+p+'" /> <table> <tr><th>Name</th><td><input name="name" /></td></tr> <tr><th>Page Type</th><td><select name="type"> <option value="0">normal</option> </select></td></tr> <tr><th>Associated Date</th><td> <input name="associated_date" class="date-human" id="newpage_date" /></td></tr> </table> </form>') .dialog({ modal:true, buttons:{ 'Create Page': function() { $('#newpage_dialog').submit(); }, 'Cancel': function() { $(this).dialog('destroy'); $(this).remove(); } } }); $('#newpage_date').each(convert_date_to_human_readable); return false;} When the add main page button is clicked, a dialog box is created asking some basic information about the page to create: We include a few hidden inputs. action: To tell the server this is an Insert Page Details action. special: When Create Page is clicked, the page will be saved in the database, but we should hide it initially so that front-end readers don't see a half-finished page. So, the special[1] flag is set (21 == 2, which is the value for hiding a page). parent: Note that this is a variable. We can use the same dialog to create sub-pages. When the dialog has been created, the date input box is converted to human-readable. Creating new sub-pages We will add sub-pages by using context menus on the page list. Note that we have a message saying right-click for options under the list. First, add this function to the /ww.admin/pages/menu.js file: function pages_add_subpage(node,tree){ var p=node[0].id.replace(/.*_/,''); pages_new(p);} We will now need to activate the context menu. This is done by adding a contextmenu plugin to the jstree plugin . Luckily, it comes with the download, so you've already installed it. Add it to the page by editing /ww.admin/pages/menu.php and add this highlighted line: <script src="/j/jquery.jstree/jquery.tree.js"></script><script src= "/j/jquery.jstree/plugins/jquery.tree.contextmenu.js"></script><script src="/ww.admin/pages/menu.js"></script> And now, we edit the .tree() call in /ww.admin/menu.js to tell it what to do: $('#pages-wrapper').tree({ callback:{// SKIPPED FOR BREVITY - DO NOT DELETE THESE LINES }, plugins:{ 'contextmenu':{ 'items':{ 'create' : { 'label' : "Create Page", 'icon' : "create", 'visible' : function (NODE, TREE_OBJ) { if(NODE.length != 1) return 0; return TREE_OBJ.check("creatable", NODE); }, 'action':pages_add_subpage, 'separator_after' : true }, 'rename':false, 'remove':false } } }}); By default, the contextmenu has three links: create, rename, and remove. You need to turn off any you're not currently using by setting them to false. Now if you right-click on any page name in the pagelist, you will have a choice to create a sub-page under it. Deleting pages We will add deletions in the same way, using the context menu. Edit the same file, and this time in the contextmenu code, replace the remove: false line with these: 'remove' : { 'label' : "Delete Page", 'icon' : "remove", 'visible' : function (NODE, TREE_OBJ) { if(NODE.length != 1) return 0; return TREE_OBJ.check("deletable", NODE); }, 'action':pages_delete, 'separator_after' : true} And add the pages_delete function to the same file: function pages_delete(node,tree){ if(!confirm( "Are you sure you want to delete this page?"))return; $.getJSON('/ww.admin/pages/delete.php?id=' +node[0].id.replace(/.*_/,''),function(){ document.location=document.location.toString(); }); } One thing to always keep in mind is whenever creating any code that deletes something in your CMS, you must ask the administrator if he/she is sure, just to make sure it wasn't an accidental click. If the administrator confirms that the click was intentional, then it's not your fault if something important was deleted. So, the pages_delete function first checks for this, and then calls the server to remove the file. The page is then refreshed because this may significantly change the page list tree, as we'll see now. Create the /ww.admin/pages/delete.php file: <?phprequire '../admin_libs.php';$id=(int)$_REQUEST['id'];if(!$id)exit;$r=dbRow("SELECT COUNT(id) AS pagecount FROM pages");if($r['pagecount']<2){ die('cannot delete - there must always be one page');}else{ $pid=dbOne("select parent from pages where id=$id",'parent'); dbQuery("delete from pages where id=$id"); dbQuery("update pages set parent=$pid where parent=$id");}echo 1; First, we ensure that there is always at least one page in the database. If deleting this page would empty the table, then we refuse to do it. There is no need to add an alert explaining this, as it should be clear to anyone that deleting the last remaining page in a website leaves the website with no content at all. Simply refusing the deletion should be enough. Next, we delete the page. Finally, any pages which were contained within that page (pages which had this one as their parent), are moved up in the tree so they are contained in the deleted page's old parent. For example, if you had a page, page1>page2>page3 (where > indicates the hierarchy), and you removed page2, then page3 would then be in the position page1>page3. This can cause a large difference in the treestructure if there were quite a few pages contained under the deleted one, so the page needs to be refreshed.
Read more
  • 0
  • 0
  • 4635

article-image-environmental-effects-3d-graphics-xna-game-studio-40
Packt
16 Dec 2010
10 min read
Save for later

Environmental Effects in 3D Graphics with XNA Game Studio 4.0

Packt
16 Dec 2010
10 min read
3D Graphics with XNA Game Studio 4.0 A step-by-step guide to adding the 3D graphics effects used by professionals to your XNA games. Improve the appearance of your games by implementing the same techniques used by professionals in the game industry Learn the fundamentals of 3D graphics, including common 3D math and the graphics pipeline Create an extensible system to draw 3D models and other effects, and learn the skills to create your own effects and animate them We will look at a technique called region growing to add plants and trees to the terrain's surface, and finish by combining the terrain with our sky box, water, and billboarding effects to create a mountain scene: Building a terrain from a heightmap A heightmap is a 2D image that stores, in each pixel, the height of the corresponding point on a grid of vertices. The pixel values range from 0 to 1, so in practice we will multiply them by the maximum height of the terrain to get the final height of each vertex. We build a terrain out of vertices and indices as a large rectangular grid with the same number of vertices as the number of pixels in the heightmap. Let's start by creating a new Terrain class. This class will keep track of everything needed to render our terrain: textures, the effect, vertex and index buffers, and so on. public class Terrain { VertexPositionNormalTexture[] vertices; // Vertex array VertexBuffer vertexBuffer; // Vertex buffer int[] indices; // Index array IndexBuffer indexBuffer; // Index buffer float[,] heights; // Array of vertex heights float height; // Maximum height of terrain float cellSize; // Distance between vertices on x and z axes int width, length; // Number of vertices on x and z axes int nVertices, nIndices; // Number of vertices and indices Effect effect; // Effect used for rendering GraphicsDevice GraphicsDevice; // Graphics device to draw with Texture2D heightMap; // Heightmap texture } The constructor will initialize many of these values: public Terrain(Texture2D HeightMap, float CellSize, float Height, GraphicsDevice GraphicsDevice, ContentManager Content) { this.heightMap = HeightMap; this.width = HeightMap.Width; this.length = HeightMap.Height; this.cellSize = CellSize; this.height = Height; this.GraphicsDevice = GraphicsDevice; effect = Content.Load<Effect>("TerrainEffect"); // 1 vertex per pixel nVertices = width * length; // (Width-1) * (Length-1) cells, 2 triangles per cell, 3 indices per // triangle nIndices = (width - 1) * (length - 1) * 6; vertexBuffer = new VertexBuffer(GraphicsDevice, typeof(VertexPositionNormalTexture), nVertices, BufferUsage.WriteOnly); indexBuffer = new IndexBuffer(GraphicsDevice, IndexElementSize.ThirtyTwoBits, nIndices, BufferUsage.WriteOnly); } Before we can generate any normals or indices, we need to know the dimensions of our grid. We know that the width and length are simply the width and height of our heightmap, but we need to extract the height values from the heightmap. We do this with the getHeights() function: private void getHeights() { // Extract pixel data Color[] heightMapData = new Color[width * length]; heightMap.GetData<Color>(heightMapData); // Create heights[,] array heights = new float[width, length]; // For each pixel for (int y = 0; y < length; y++) for (int x = 0; x < width; x++) { // Get color value (0 - 255) float amt = heightMapData[y * width + x].R; // Scale to (0 - 1) amt /= 255.0f; // Multiply by max height to get final height heights[x, y] = amt * height; } } This will initialize the heights[,] array, which we can then use to build our vertices. When building vertices, we simply lay out a vertex for each pixel in the heightmap, spaced according to the cellSize variable. Note that this will create (width – 1) * (length – 1) "cells"—each with two triangles: The function that does this is as shown: private void createVertices() { vertices = new VertexPositionNormalTexture[nVertices]; // Calculate the position offset that will center the terrain at (0, 0, 0) Vector3 offsetToCenter = -new Vector3(((float)width / 2.0f) * cellSize, 0, ((float)length / 2.0f) * cellSize); // For each pixel in the image for (int z = 0; z < length; z++) for (int x = 0; x < width; x++) { // Find position based on grid coordinates and height in // heightmap Vector3 position = new Vector3(x * cellSize, heights[x, z], z * cellSize) + offsetToCenter; // UV coordinates range from (0, 0) at grid location (0, 0) to // (1, 1) at grid location (width, length) Vector2 uv = new Vector2((float)x / width, (float)z / length); // Create the vertex vertices[z * width + x] = new VertexPositionNormalTexture( position, Vector3.Zero, uv); } } When we create our terrain's index buffer, we need to lay out two triangles for each cell in the terrain. All we need to do is find the indices of the vertices at each corner of each cell, and create the triangles by specifying those indices in clockwise order for two triangles. For example, to create the triangles for the first cell in the preceding screenshot, we would specify the triangles as [0, 1, 4] and [4, 1, 5]. private void createIndices() { indices = new int[nIndices]; int i = 0; // For each cell for (int x = 0; x < width - 1; x++) for (int z = 0; z < length - 1; z++) { // Find the indices of the corners int upperLeft = z * width + x; int upperRight = upperLeft + 1; int lowerLeft = upperLeft + width; int lowerRight = lowerLeft + 1; // Specify upper triangle indices[i++] = upperLeft; indices[i++] = upperRight; indices[i++] = lowerLeft; // Specify lower triangle indices[i++] = lowerLeft; indices[i++] = upperRight; indices[i++] = lowerRight; } } The last thing we need to calculate for each vertex is the normals. Because we are creating the terrain from scratch, we will need to calculate all of the normals based only on the height data that we are given. This is actually much easier than it sounds: to calculate the normals we simply calculate the normal of each triangle of the terrain and add that normal to each vertex involved in the triangle. Once we have done this for each triangle, we simply normalize again, averaging the influences of each triangle connected to each vertex. private void genNormals() { // For each triangle for (int i = 0; i < nIndices; i += 3) { // Find the position of each corner of the triangle Vector3 v1 = vertices[indices[i]].Position; Vector3 v2 = vertices[indices[i + 1]].Position; Vector3 v3 = vertices[indices[i + 2]].Position; // Cross the vectors between the corners to get the normal Vector3 normal = Vector3.Cross(v1 - v2, v1 - v3); normal.Normalize(); // Add the influence of the normal to each vertex in the // triangle vertices[indices[i]].Normal += normal; vertices[indices[i + 1]].Normal += normal; vertices[indices[i + 2]].Normal += normal; } // Average the influences of the triangles touching each // vertex for (int i = 0; i < nVertices; i++) vertices[i].Normal.Normalize(); } We'll finish off the constructor by calling these functions in order and then setting the vertices and indices that we created into their respective buffers: createVertices(); createIndices(); genNormals(); vertexBuffer.SetData<VertexPositionNormalTexture>(vertices); indexBuffer.SetData<int>(indices); Now that we've created the framework for this class, let's create the TerrainEffect.fx effect. This effect will, for the moment, be responsible for some simple directional lighting and texture mapping. We'll need a few effect parameters: float4x4 View; float4x4 Projection; float3 LightDirection = float3(1, -1, 0); float TextureTiling = 1; texture2D BaseTexture; sampler2D BaseTextureSampler = sampler_state { Texture = <BaseTexture>; AddressU = Wrap; AddressV = Wrap; MinFilter = Anisotropic; MagFilter = Anisotropic; }; The TextureTiling parameter will determine how many times our texture is repeated across the terrain's surface—simply stretching it across the terrain would look bad because it would need to be stretched to a very large size. "Tiling" it across the terrain will look much better. We will need a very standard vertex shader: struct VertexShaderInput { float4 Position : POSITION0; float2 UV : TEXCOORD0; float3 Normal : NORMAL0; }; struct VertexShaderOutput { float4 Position : POSITION0; float2 UV : TEXCOORD0; float3 Normal : TEXCOORD1; }; VertexShaderOutput VertexShaderFunction(VertexShaderInput input) { VertexShaderOutput output; output.Position = mul(input.Position, mul(View, Projection)); output.Normal = input.Normal; output.UV = input.UV; return output; } The pixel shader is also very standard, except that we multiply the texture coordinates by the TextureTiling parameter. This works because the texture sampler's address mode is set to "wrap", and thus the sampler will simply wrap the texture coordinates past the edge of the texture, creating the tiling effect. float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0 { float light = dot(normalize(input.Normal), normalize(LightDirection)); light = clamp(light + 0.4f, 0, 1); // Simple ambient lighting float3 tex = tex2D(BaseTextureSampler, input.UV * TextureTiling); return float4(tex * light, 1); } The technique definition is the same as our other effects: technique Technique1 { pass Pass1 { VertexShader = compile vs_2_0 VertexShaderFunction(); PixelShader = compile ps_2_0 PixelShaderFunction(); } } In order to use the effect with our terrain, we'll need to add a few more member variables to the Terrain class: Texture2D baseTexture; float textureTiling; Vector3 lightDirection; These values will be set from the constructor: public Terrain(Texture2D HeightMap, float CellSize, float Height, Texture2D BaseTexture, float TextureTiling, Vector3 LightDirection, GraphicsDevice GraphicsDevice, ContentManager Content) { this.baseTexture = BaseTexture; this.textureTiling = TextureTiling; this.lightDirection = LightDirection; // etc... Finally, we can simply set these effect parameters along with the View and Projection parameters in the Draw() function: effect.Parameters["BaseTexture"].SetValue(baseTexture); effect.Parameters["TextureTiling"].SetValue(textureTiling); effect.Parameters["LightDirection"].SetValue(lightDirection); Let's now add the terrain to our game. We'll need a new member variable in the Game1 class: Terrain terrain; We'll need to initialize it in the LoadContent() method: terrain = new Terrain(Content.Load<Texture2D>("terrain"), 30, 4800, Content.Load<Texture2D>("grass"), 6, new Vector3(1, -1, 0), GraphicsDevice, Content); Finally, we can draw it in the Draw() function: terrain.Draw(camera.View, camera.Projection); Multitexturing Our terrain looks pretty good as it is, but to make it more believable the texture applied to it needs to vary—snow and rocks at the peaks, for example. To do this, we will use a technique called multitexturing, which uses the red, blue, and green channels of a texture as a guide as to where to draw textures that correspond to those channels. For example, sand may correspond to red, snow to blue, and rock to green. Adding snow would then be as simple as painting blue onto the areas of this "texture map" that correspond with peaks on the heightmap. We will also have one extra texture that fills in the area where no colors have been painted onto the texture map—grass, for example. To begin with, we will need to modify our texture parameters on our effect from one texture to five: the texture map, the base texture, and the three color channel mapped textures. texture RTexture; sampler RTextureSampler = sampler_state { texture = <RTexture>; AddressU = Wrap; AddressV = Wrap; MinFilter = Anisotropic; MagFilter = Anisotropic; }; texture GTexture; sampler GTextureSampler = sampler_state { texture = <GTexture>; AddressU = Wrap; AddressV = Wrap; MinFilter = Anisotropic; MagFilter = Anisotropic; }; texture BTexture; sampler BTextureSampler = sampler_state { texture = <BTexture>; AddressU = Wrap; AddressV = Wrap; MinFilter = Anisotropic; MagFilter = Anisotropic; }; texture BaseTexture; sampler BaseTextureSampler = sampler_state { texture = <BaseTexture>; AddressU = Wrap; AddressV = Wrap; MinFilter = Anisotropic; MagFilter = Anisotropic; }; texture WeightMap; sampler WeightMapSampler = sampler_state { texture = <WeightMap>; AddressU = Clamp; AddressV = Clamp; MinFilter = Linear; MagFilter = Linear; }; Second, we need to update our pixel shader to draw these textures onto the terrain: float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0 { float light = dot(normalize(input.Normal), normalize( LightDirection)); light = clamp(light + 0.4f, 0, 1); float3 rTex = tex2D(RTextureSampler, input.UV * TextureTiling); float3 gTex = tex2D(GTextureSampler, input.UV * TextureTiling); float3 bTex = tex2D(BTextureSampler, input.UV * TextureTiling); float3 base = tex2D(BaseTextureSampler, input.UV * TextureTiling); float3 weightMap = tex2D(WeightMapSampler, input.UV); float3 output = clamp(1.0f - weightMap.r - weightMap.g - weightMap.b, 0, 1); output *= base; output += weightMap.r * rTex + weightMap.g * gTex + weightMap.b * bTex; return float4(output * light, 1); } We'll need to add a way to set these values to the Terrain class: public Texture2D RTexture, BTexture, GTexture, WeightMap; All we need to do now is set these values to the effect in the Draw() function: effect.Parameters["RTexture"].SetValue(RTexture); effect.Parameters["GTexture"].SetValue(GTexture); effect.Parameters["BTexture"].SetValue(BTexture); effect.Parameters["WeightMap"].SetValue(WeightMap); To use multitexturing in our game, we'll need to set these values in the Game1 class: terrain.WeightMap = Content.Load<Texture2D>("weightMap"); terrain.RTexture = Content.Load<Texture2D>("sand"); terrain.GTexture = Content.Load<Texture2D>("rock"); terrain.BTexture = Content.Load<Texture2D>("snow");
Read more
  • 0
  • 0
  • 6602

article-image-oracle-apex-plug-ins
Packt
16 Dec 2010
6 min read
Save for later

Oracle APEX Plug-ins

Packt
16 Dec 2010
6 min read
Oracle APEX 4.0 Cookbook Over 80 great recipes to develop and deploy fast, secure, and modern web applications with Oracle Application Express 4.0 Create feature-rich web applications in APEX 4.0 Integrate third-party applications like Google Maps into APEX by using web services Enhance APEX applications by using stylesheets, Plug-ins, Dynamic Actions, AJAX, JavaScript, BI Publisher, and jQuery Hands-on examples to make the most out of the possibilities that APEX has to offer Part of Packt's Cookbook series: Each recipe is a carefully organized sequence of instructions to complete the task as efficiently as possible   Introduction In APEX 4.0, Oracle introduced the plug-in. A plug-in is an extension to the existing functionality of APEX. The idea behind plug-ins is to make life easier for developers. Plug-ins are reusable and can be exported and imported. In this way, it is possible to create functionality which is available to all APEX developers. It is also possible to install and use them without having knowledge of what is inside the plug-in. APEX is actually a program that converts your settings from the APEX builder to HTML and JavaScript. For example, if you created a text item in the APEX builder, APEX converts this to the following code (simplified): <input type="text" id="P12_NAME" name="P12_NAME" value="your name"> When you create an item type plug-in, you actually take over this conversion task of APEX and you generate the HTML and JavaScript code yourself by using PL/SQL procedures. That offers a lot of flexibility because now you can make this code generic so that it can be used for more items. The same goes for region type plug-ins. A region is a container for forms, reports, and such. The region can be a div or a HTML table. By creating a region type plug-in, you create a region yourself with the possibility to add more functionality to the region. There are four types of plug-in: Item type plug-ins Region type plug-ins Dynamic action plug-ins Process type plug-ins In this article, we will discuss all four types of plug-in. Creating an item type plug-in In an item type plug-in you create an item with the possibility of extending its functionality. To demonstrate this, we will make a text field with a tooltip. This functionality is already available in APEX 4.0 by adding the following code to the HTML form element attributes text field in the Element section of the text field: onmouseover="toolTip_enable(event,this,'A tooltip')" But you have to do this for every item that should contain a tooltip. This can be made more easy by creating an item type plug-in with a built-in tooltip. And if you create an item of type plug-in, you will be asked to enter some text for the tooltip. Getting ready For this recipe, you can use an existing page with a region where you can put some text items on. How to do it... Go to Shared Components | User Interface | Plug-ins. Click on the Create button. In the name section, enter a name in the name text field. In this case, we enter tooltip. In the internal name text field, enter an internal name. It is advised to use your company's domain address reversed to ensure the name is unique when you decide to share this plug-in. So, for example, you can use com.packtpub.apex.tooltip. In the source section, enter the following code to the PL/SQL code textarea: function render_simple_tooltip ( p_item in apex_plugin.t_page_item , p_plugin in apex_plugin.t_plugin , p_value in varchar2 , p_is_readonly in boolean , p_is_printer_friendly in boolean ) return apex_plugin.t_page_item_render_result is l_result apex_plugin.t_page_item_render_result; begin if apex_application.g_debug then apex_plugin_util.debug_page_item ( p_plugin => p_plugin , p_page_item => p_item , p_value => p_value , p_is_readonly => p_is_readonly , p_is_printer_friendly => p_is_printer_friendly); end if; -- sys.htp.p('<input type="text" id="'||p_item.name||'" name="'||p_item.name||'" class="text_field" onmouseover="toolTip_enable(event,this,'||''''||p_item.attribute_01||''''||')">'); -- return l_result; end render_simple_tooltip; This function uses the sys.htp.p function to put a text item on the screen. On the text item, the onmouseover event calls the function tooltip_enable(). This function is an APEX function and can be used to put a tooltip on an item. The arguments of the function are mandatory. The function starts with the option to show debug information. This can be very useful when you have created a plug-in and it doesn't work. After the debug information the htp.p function puts the text item on the screen, including the call to tooltip_enable. You can also see that the call to tooltip_enable uses p_item.attribute_01. This is a parameter that you can use to pass a value to the plug-in. That is the following step in this recipe. The function ends with the return of l_result. This variable is of type apex_plugin.t_page_item_render_result. For the other types of plug-in there are also dedicated return types, for example, t_region_render_result. Click on the Create button. The next step is to define the parameter (attribute) for this plug-in. In the Custom Attributes section, click the Add Attribute button. In the name section, enter a name in the label text field, for example tooltip. Ensure that the attribute text field contains the value 1. In the settings section, set the type to text. Click on the Create button. In the callbacks section, enter render_simple_tooltip into the render function name text field. Click on the Apply changes button. The plug-in is ready now. The next step is to create an item of type tooltip plug-in. Go to a page with a region where you want to use an item with a tooltip. In the items section, click on the add icon to create a new item. Select Plug-ins. Now you will get a list of available plug-ins. Select the one we just created, tooltip. Click on Next. In the item name text field, enter a name for the item, for example tt_item. In the region select list, select the region you want to put the item in. Click Next. In the next step, you will get a new option. It's the attribute you created with the plug-in. Enter the tooltip text here. Click Next. In the last step, leave everything as it is and click the Create item button. You are ready now. Run the page. When you move your mouse pointer over the new item, you will see the tooltip.
Read more
  • 0
  • 0
  • 4778

article-image-packt-enterprise-special-999-ebook-offer
Packt
16 Dec 2010
1 min read
Save for later

Packt Enterprise Special $9.99 eBook Offer

Packt
16 Dec 2010
1 min read
Since their release these books have proved incredibly popular, and are reflective of Packt’s unique ability to publish books on a range of specialist subjects. Maximize Your Investment: 10 Key Strategies for Effective Packaged Software Implementations is aimed at enterprise architects, development leads, project managers, business systems analysts, business systems owners, and anyone who wants to implement packaged software effectively.  It enables the reader to accelerate packaged (COTS) software implementations, increase returns on investment, and reduce implementation costs and customizations.     In contrast Microsoft Office Live Small Business: Beginner’s Guide, as the title suggests, is for small-business owners who don’t necessarily have any technical knowledge, but want to build and customize their business web sites on Microsoft’s free-to-use platform.  At just $9.99 this eBook will provide you with all you need to know to build a professional and sleek website to represent your business, enabling you to attract more customers and increase sales.    
Read more
  • 0
  • 0
  • 1235

article-image-python-text-processing-nltk-2-transforming-chunks-and-trees
Packt
16 Dec 2010
10 min read
Save for later

Python Text Processing with NLTK 2: Transforming Chunks and Trees

Packt
16 Dec 2010
10 min read
  Python Text Processing with NLTK 2.0 Cookbook Use Python's NLTK suite of libraries to maximize your Natural Language Processing capabilities. Quickly get to grips with Natural Language Processing – with Text Analysis, Text Mining, and beyond Learn how machines and crawlers interpret and process natural languages Easily work with huge amounts of data and learn how to handle distributed processing Part of Packt's Cookbook series: Each recipe is a carefully organized sequence of instructions to complete the task as efficiently as possible Introduction This article will show you how to do various transforms on both chunks and trees. The chunk transforms are for grammatical correction and rearranging phrases without loss of meaning. The tree transforms give you ways to modify and flatten deep parse trees. The functions detailed in these recipes modify data, as opposed to learning from it. That means it's not safe to apply them indiscriminately. A thorough knowledge of the data you want to transform, along with a few experiments, should help you decide which functions to apply and when. Whenever the term chunk is used in this article, it could refer to an actual chunk extracted by a chunker, or it could simply refer to a short phrase or sentence in the form of a list of tagged words. What's important in this article is what you can do with a chunk, not where it came from. Filtering insignificant words Many of the most commonly used words are insignificant when it comes to discerning the meaning of a phrase. For example, in the phrase "the movie was terrible", the most significant words are "movie" and "terrible", while "the" and "was" are almost useless. You could get the same meaning if you took them out, such as "movie terrible" or "terrible movie". Either way, the sentiment is the same. In this recipe, we'll learn how to remove the insignificant words, and keep the significant ones, by looking at their part-of-speech tags. Getting ready First, we need to decide which part-of-speech tags are significant and which are not. Looking through the treebank corpus for stopwords yields the following table of insignificant words and tags:   Word Tag a DT all PDT an DT and CC or CC that WDT the DT Other than CC, all the tags end with DT. This means we can filter out insignificant words by looking at the tag's suffix. How to do it... In transforms.py there is a function called filter_insignificant(). It takes a single chunk, which should be a list of tagged words, and returns a new chunk without any insignificant tagged words. It defaults to filtering out any tags that end with DT or CC. def filter_insignificant(chunk, tag_suffixes=['DT', 'CC']): good = [] for word, tag in chunk: ok = True for suffix in tag_suffixes: if tag.endswith(suffix): ok = False break if ok: good.append((word, tag)) return good Now we can use it on the part-of-speech tagged version of "the terrible movie". >>> from transforms import filter_insignificant >>> filter_insignificant([('the', 'DT'), ('terrible', 'JJ'), ('movie', 'NN')]) [('terrible', 'JJ'), ('movie', 'NN')] As you can see, the word "the" is eliminated from the chunk. How it works... filter_insignificant() iterates over the tagged words in the chunk. For each tag, it checks if that tag ends with any of the tag_suffixes. If it does, then the tagged word is skipped. However if the tag is ok, then the tagged word is appended to a new good chunk that is returned. There's more... The way filter_insignificant() is defined, you can pass in your own tag suffixes if DT and CC are not enough, or are incorrect for your case. For example, you might decide that possessive words and pronouns such as "you", "your", "their", and "theirs" are no good but DT and CC words are ok. The tag suffixes would then be PRP and PRP$. Following is an example of this function: >>> filter_insignificant([('your', 'PRP$'), ('book', 'NN'), ('is', 'VBZ'), ('great', 'JJ')], tag_suffixes=['PRP', 'PRP$']) [('book', 'NN'), ('is', 'VBZ'), ('great', 'JJ')] Filtering insignificant words can be a good complement to stopword filtering for purposes such as search engine indexing, querying, and text classification. Correcting verb forms It's fairly common to find incorrect verb forms in real-world language. For example, the correct form of "is our children learning?" is "are our children learning?". The verb "is" should only be used with singular nouns, while "are" is for plural nouns, such as "children". We can correct these mistakes by creating verb correction mappings that are used depending on whether there's a plural or singular noun in the chunk. Getting ready We first need to define the verb correction mappings in transforms.py. We'll create two mappings, one for plural to singular, and another for singular to plural. plural_verb_forms = { ('is', 'VBZ'): ('are', 'VBP'), ('was', 'VBD'): ('were', 'VBD') } singular_verb_forms = { ('are', 'VBP'): ('is', 'VBZ'), ('were', 'VBD'): ('was', 'VBD') } Each mapping has a tagged verb that maps to another tagged verb. These initial mappings cover the basics of mapping, is to are, was to were, and vice versa. How to do it... In transforms.py there is a function called correct_verbs(). Pass it a chunk with incorrect verb forms, and you'll get a corrected chunk back. It uses a helper function first_chunk_index() to search the chunk for the position of the first tagged word where pred returns True. def first_chunk_index(chunk, pred, start=0, step=1): l = len(chunk) end = l if step > 0 else -1 for i in range(start, end, step): if pred(chunk[i]): return i return None def correct_verbs(chunk): vbidx = first_chunk_index(chunk, lambda (word, tag): tag. startswith('VB')) # if no verb found, do nothing if vbidx is None: return chunk verb, vbtag = chunk[vbidx] nnpred = lambda (word, tag): tag.startswith('NN') # find nearest noun to the right of verb nnidx = first_chunk_index(chunk, nnpred, start=vbidx+1) # if no noun found to right, look to the left if nnidx is None: nnidx = first_chunk_index(chunk, nnpred, start=vbidx-1, step=-1) # if no noun found, do nothing if nnidx is None: return chunk noun, nntag = chunk[nnidx] # get correct verb form and insert into chunk if nntag.endswith('S'): chunk[vbidx] = plural_verb_forms.get((verb, vbtag), (verb, vbtag)) else: chunk[vbidx] = singular_verb_forms.get((verb, vbtag), (verb, vbtag)) return chunk When we call it on a part-of-speech tagged "is our children learning" chunk, we get back the correct form, "are our children learning". >>> from transforms import correct_verbs >>> correct_verbs([('is', 'VBZ'), ('our', 'PRP$'), ('children', 'NNS'), ('learning', 'VBG')]) [('are', 'VBP'), ('our', 'PRP$'), ('children', 'NNS'), ('learning', 'VBG')] We can also try this with a singular noun and an incorrect plural verb. >>> correct_verbs([('our', 'PRP$'), ('child', 'NN'), ('were', 'VBD'), ('learning', 'VBG')]) [('our', 'PRP$'), ('child', 'NN'), ('was', 'VBD'), ('learning', 'VBG')] In this case, "were" becomes "was" because "child" is a singular noun. How it works... The correct_verbs() function starts by looking for a verb in the chunk. If no verb is found, the chunk is returned with no changes. Once a verb is found, we keep the verb, its tag, and its index in the chunk. Then we look on either side of the verb to find the nearest noun, starting on the right, and only looking to the left if no noun is found on the right. If no noun is found at all, the chunk is returned as is. But if a noun is found, then we lookup the correct verb form depending on whether or not the noun is plural. Plural nouns are tagged with NNS, while singular nouns are tagged with NN. This means we can check the plurality of a noun by seeing if its tag ends with S. Once we get the corrected verb form, it is inserted into the chunk to replace the original verb form. To make searching through the chunk easier, we define a function called first_chunk_ index(). It takes a chunk, a lambda predicate, the starting index, and a step increment. The predicate function is called with each tagged word until it returns True. If it never returns True, then None is returned. The starting index defaults to zero and the step increment to one. As you'll see in upcoming recipes, we can search backwards by overriding start and setting step to -1. This small utility function will be a key part of subsequent transform functions. Swapping verb phrases Swapping the words around a verb can eliminate the passive voice from particular phrases. For example, "the book was great" can be transformed into "the great book". How to do it... In transforms.py there is a function called swap_verb_phrase(). It swaps the right-hand side of the chunk with the left-hand side, using the verb as the pivot point. It uses the first_chunk_index() function defined in the previous recipe to find the verb to pivot around. def swap_verb_phrase(chunk): # find location of verb vbpred = lambda (word, tag): tag != 'VBG' and tag.startswith('VB') and len(tag) > 2 vbidx = first_chunk_index(chunk, vbpred) if vbidx is None: return chunk return chunk[vbidx+1:] + chunk[:vbidx] Now we can see how it works on the part-of-speech tagged phrase "the book was great". >>> from transforms import swap_verb_phrase >>> swap_verb_phrase([('the', 'DT'), ('book', 'NN'), ('was', 'VBD'), ('great', 'JJ')]) [('great', 'JJ'), ('the', 'DT'), ('book', 'NN')] The result is "great the book". This phrase clearly isn't grammatically correct, so read on to learn how to fix it. How it works... Using first_chunk_index() from the previous recipe, we start by finding the first matching verb that is not a gerund (a word that ends in "ing") tagged with VBG. Once we've found the verb, we return the chunk with the right side before the left, and remove the verb. The reason we don't want to pivot around a gerund is that gerunds are commonly used to describe nouns, and pivoting around one would remove that description. Here's an example where you can see how not pivoting around a gerund is a good thing: >>> swap_verb_phrase([('this', 'DT'), ('gripping', 'VBG'), ('book', 'NN'), ('is', 'VBZ'), ('fantastic', 'JJ')]) [('fantastic', 'JJ'), ('this', 'DT'), ('gripping', 'VBG'), ('book', 'NN')] If we had pivoted around the gerund, the result would be "book is fantastic this", and we'd lose the gerund "gripping". There's more... Filtering insignificant words makes the final result more readable. By filtering either before or after swap_verb_phrase(), we get "fantastic gripping book" instead of "fantastic this gripping book". >>> from transforms import swap_verb_phrase, filter_insignificant >>> swap_verb_phrase(filter_insignificant([('this', 'DT'), ('gripping', 'VBG'), ('book', 'NN'), ('is', 'VBZ'), ('fantastic', 'JJ')])) [('fantastic', 'JJ'), ('gripping', 'VBG'), ('book', 'NN')] >>> filter_insignificant(swap_verb_phrase([('this', 'DT'), ('gripping', 'VBG'), ('book', 'NN'), ('is', 'VBZ'), ('fantastic', 'JJ')])) [('fantastic', 'JJ'), ('gripping', 'VBG'), ('book', 'NN')] Either way, we get a shorter grammatical chunk with no loss of meaning.
Read more
  • 0
  • 0
  • 3870

article-image-documentum-65-content-management-foundations-packt-enterprises-book-moment
Packt
16 Dec 2010
1 min read
Save for later

Documentum 6.5 Content Management Foundations - Packt Enterprise's Book of the Moment

Packt
16 Dec 2010
1 min read
Packt Enterprise's Book of the Moment Packt Enterprise's book of the moment has been named as... Documentum 6.5 Content Management Foundations This book has been awarded Packt Enterprise's Book of the Moment due to its continued success and a myriad of positive reviews. Written by Pawan Kumar  Technical Architect with current expertise in Enterprise Content Management with EMC Documentum, this book will help you learn the technical fundamentals of Documentum 6.5, develop insights with illustrated examples from a real-life business scenario, and ace the E20-120 exam  
Read more
  • 0
  • 0
  • 1242
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-getting-started-magento-development
Packt
15 Dec 2010
8 min read
Save for later

Getting Started with Magento Development

Packt
15 Dec 2010
8 min read
Magento is an open-source eCommerce stack with smart features such as layered navigation, auto-complete search, multiple language support, multiple stores, smart browsing, RSS product feeds, tagging and reviewing products, reviewing search terms, reviewing customer tags, poll manager, currency exchange rates, Google sitemap integration, abandoned shopping cart report, catalog-wide product comparisons, product wish lists, and even zooming the product image. It was created by Varien, based on the top notch MVC framework—Zend Framework, on March 31, 2008. As with other MVC applications, Magento keeps the display logic separated from the application's business logic. A dedicated team works on updating Magento regularly. Magento Developer is a very hands-on role for both a novice and experienced software engineer who is interested in achieving high impact in a fast-paced development environment with an ambitious mission. Magento gives a very handy way to deal with its features faster than any other alternatives, whether you know a little or a lot about PHP programming. Let's see how these things happen. We will make our development platform ready to cook some mouth-watering recipes for Magento in this article. If you are a newbie, this is the right place to start. If you are a pro, this is still the right place to start as we have tried to follow some best practices for Magento development that you might not be aware of. Let's get our hands dirty with Magento Development! Good luck! Preparing the platform with a virtual host Magento is built on the de facto PHP framework—Zend Framework. We need to set up our development environment properly to get most out of it. In this recipe, we will set up a Fully Qualifed Domain Name (FQDN) and a virtual host. We could use the domain as http://localhost/magento or something like this, but in that case accessing the admin panel might be cumbersome. In most cases, you have to access through the local IP address. Using a FQDN, you don't have to worry about it. A FQDN will make the debugging process much easier also. Getting Ready We need to have the following things installed before kicking off the setup of a virtual host a.k.a. vhost: Apache2 PHP5 MySQL server 5.0 If we want to install the previously mentioned tools from Ubuntu Linux CLI, we can easily do that by running the following commands. We will be using Linux commands based on Ubuntu. The following command is a basic Apache installation command: sudo aptitude install apache2 apache2.2-common apache2-mpm-prefork apache2-utils libexpat1 ssl-cert The following command is a basic PHP5 installation command: sudo aptitude install libapache2-mod-php5 php5 php5-common php5-curl php5-dev php5-gd php5-imagick php5-mcrypt php5-memcache php5-mhash php5- mysql php5-pspell php5-snmp php5-sqlite php5-xmlrpc php5-xsl Enter the following command to begin with a simple MySQL installation: sudo aptitude install mysql-server mysql-client libmysqlclient15-dev Note that we have installed the development libs and headers with the libmysqlclient15-dev package. You can leave that out but it has been found that they are useful in many situations. Alternately, we can use an all-in-one package, such as the XAMPP, to get all the aforementioned tools in one click. The XAMPP package can be downloaded from http://www.apachefriends.org/en/xampp.html. How to do it... To test the domain without creating a DNS zone and record(s) on some Internet nameserver(s), let's modify the /etc/hosts file on our local computer to include some entries mapping the magento.local.com, and so on to the local machine's public IP address (in most cases, 127.0.0.1). Open the /etc/hosts file with any text editor or run the following command in terminal: sudo nano /etc/hosts Add the following line as a similar entry in /etc/hosts file: 127.0.0.1 magento.local.com The location of the host file depends on the OS loaded. If it's a Windows OS it should be c:windowssystem32driversetchosts. Now let's create the layout for our domain. Open terminal and change your location to the WEBROOT directory by running the following command: cd /var/www/ If you are using XAMPP, it would be c:xampphtdocs. Now for our domain, we want to host/create a folder with a standard set of subfolders: mkdir -p magento.local.com/{public,private,log,cgi-bin,backup} Let's create an index.html file for our domain: sudo nano /var/www/magento.local.com/public/index.html It's time to put some content in our index file: <html> <head> <title>Welcome to magento.local.com</title> </head> <body> <h1>Welcome to magento.local.com</h1> </body> </html> We've set up the basics and now we're ready to add our own virtual hosts, so that we can start to serve our domain. Let's go ahead and create the vhost file for magento.local.com: sudo nano /etc/apache2/sites-available/magento.local.com The contents should look like this: # Place any notes or comments you have here # It will make any customization easier to understand in the weeks to come # domain: magento.local.com # public: /var/www/magento.local.com/public <VirtualHost *:80> # Admin email, Server Name (domain name) and any aliases ServerAdmin webmaster@magento.local.com ServerName magento.local.com # Index file and Document Root (where the public files are located) DirectoryIndex index.php index.html DocumentRoot /var/www/magento.local.com/public # Custom log file locations LogLevel warn ErrorLog /var/www/magento.local.com/log/error.log CustomLog /var/www/magento.local.com/log/access.log combined </VirtualHost> Now we have the site available, we need to enable it: sudo a2ensite magento.local.com It seems like good advice to reload the apache: sudo /etc/init.d/apache2 reload With such changes made, we can now navigate to our site in a web browser on our local computer, as seen in the following screenshot: Tada! We now have the contents of public/index.html being shown. How it works... The Getting ready recipe describes the installation process of Apache, PHP, and MySQL from Linux command line. If you have already installed those, you can skip it and start confguring the layout and vhost directly. The Magento source files will be put in the public directory. Other directories represent as their name suggest. Usually in production server, we don't have to write the domain name manually as we wrote here, as the DNS zone is there. The rest of this recipe is the same as the production server. The virtual host contents has some inline comments starting with # to describe the purpose. Setting up a Subversion/SVN A Subversion is a popular version control system initiated in 2000 by CollabNet Inc. Its goal is to be a mostly-compatible successor to the widely used Concurrent Versions System (CVS). Getting Ready If you are a Windows user, please download the TortoiseSVN client from here: http://tortoisesvn.tigris.org/ and if you are a Linux user, fre up your terminal. How to do it... Execute the following command in the terminal to install Subversion: sudo apt-get install subversion Make sure you have an active Internet connection as aptitude will install the package from Ubuntu repository. If you are using windows and intended to use TortoiseSVN you may get a Windows binary from http://tortoisesvn.tigris.org. After downloading the TortoiseSVN installer file, double-click on the TortoiseSVN installer file and follow the on screen instructions. Now let's add a new group called Subversion to our system and add current username to Subversion group. sudo addgroup subversion sudo adduser <your_username_here> subversion If you don't a have a clue about your current username, issue the command whoami in your terminal. Once the Subversion installation is completed, let's create our repository for Magento project. Now issue the following command in the terminal: sudo mkdir /home/svn cd /home/svn sudo mkdir magento sudo chown -R www-data:subversion magento sudo chmod -R g+rws magento Now initiate an SVN repository with the following command: sudo svnadmin create /home/svn/magento In the case of TortoiseSVN, open the folder where you want to put your Magento repository and right-click and find create repository here... from the context menu TortoiseSVN. Enter a name for your repository, in this case the repository name would be magento. It's time to change your current working directory in terminal to: cd /var/www/magento.local.com We will checkout the Magento project by issuing the following command in the terminal: svn co file:///home/svn/magento public How it works... When we execute any apt-get command, the aptitude will take care of the installation process. The aptitude will look for the package from Ubuntu central repository—and download it if necessary—and perform the necessary tasks. After completion of the installation process, we've created a new group named subversion and added the username to the newly created group subversion. Then we've created a directory under /home/location to hold all repositories. After that, we've created a new repository for our Magento project named magento. Finally, we've checked out the repository in our site location in the public directory.
Read more
  • 0
  • 0
  • 2446

article-image-tips-and-tricks-effectively-using-aspnet
Packt
15 Dec 2010
4 min read
Save for later

Tips and Tricks for Effectively Using ASP.NET

Packt
15 Dec 2010
4 min read
ASP.NET Site Performance Secrets Simple and proven techniques to quickly speed up your ASP.NET website Speed up your ASP.NET website by identifying performance bottlenecks that hold back your site's performance and fixing them Tips and tricks for writing faster code and pinpointing those areas in the code that matter most, thus saving time and energy Drastically reduce page load times Configure and improve compression – the single most important way to improve your site's performance Written in a simple problem-solving manner – with a practical hands-on approach and just the right amount of theory you need to make sense of it all        It is good to monitor the performance of your site continuously Tip: The performance of your website is affected by both the things you control, such as code changes, and the things you cannot control such as increases in the number of visitors or server problems. Because of this, it makes sense to monitor the performance of your site continuously. That way, you find out that the site is becoming too slow before your manager does. It is important to reduce the "time to first byte" Tip: The "time to first byte" is the time it takes your server to generate a page, plus the time taken to move the first byte over the Internet to the browser. Reducing that time is important for visitor retention—you want to give them something to look at, and provide confidence that they'll have the page in their browser soon. It involves making better use of system resources such as memory and CPU. Caching is one of the methods used to improve website performance Tip: Caching allows you to store individual objects, parts of web pages, or entire web pages in memory either in the browser, a proxy, or the server. That way, those objects or pages do not have to be generated again for each request, giving you: Reduced response time Reduced memory and CPU usage Less load on the database Fewer round trips to the server, when using browser or proxy caching Reduced retrieval times when the content is served from proxy cache, by bringing the contents closer to the browser Building projects in the Release mode is a good practice Tip: If your site is a web-application project rather than a website, or if your website is a part of a solution containing other projects, be sure to build your releases in the release mode. This removes debugging overhead from your code, so it uses less CPU and memory. It is important to reduce Round Trips between browser and server Tip: Round trips between browser and server can take a long time, increasing wait times for the visitor. Hence it is necessary to cut down on them. The same goes for round trips between web server and database server. Permanent redirects Tip: If you are redirecting visitors to a new page because the page is outdated, use a permanent 301 redirect. Browsers and proxies will update their caches, and search engines will use them as well. That way, you reduce traffic to the old page. You can issue a 301 redirect programmatically: Response.StatusCode = 301; Response.AddHeader("Location", "NewPage.aspx"); Response.End(); For .NET 4 or higher: Response.RedirectPermanent("NewPage.aspx");   Hotlinking should be avoided Tip: Hotlinking is the practice of linking to someone else's images on your site. If this happens to you and your images, another web master gets to show your images on their site and you get to pay for the additional bandwidth and incur the additional load on your server. A great little module that prevents hot linking is LeechGuard Hot-Linking Prevention Module at http://www.iis.net/community/default.aspx?tabid=34&i=1288&g=6. Session state is taking too much memory Tip: If you decide that session state is taking too much memory, here are some solutions. Reduce session state life time Reduce space taken by session state Use another session mode Stop using session state Cookies Tip: ASP.NET disables all output caching if you set a cookie, to make sure the cookie isn't sent to the wrong visitor. Since setting cookies and proxy caching also don't go together performance-wise, you'll want to keep setting the number of cookies to a minimum. This can be done by trying not to set a cookie on every request but only when strictly needed. Minimizing the duration of locks Tip: Acquire locks on shared resources just before you access them, and release them immediately after you are finished with them. By limiting the time each resource is locked, you minimize the time threads need to wait for resources to become available.
Read more
  • 0
  • 0
  • 8025

article-image-openscenegraph-managing-scene-graph
Packt
15 Dec 2010
9 min read
Save for later

OpenSceneGraph: Managing Scene Graph

Packt
15 Dec 2010
9 min read
  OpenSceneGraph 3.0: Beginner's Guide Create high-performance virtual reality applications with OpenSceneGraph, one of the best 3D graphics engines. The Group interface The osg::Group type represents the group nodes of an OSG scene graph. It can have any number of child nodes, including the osg::Geode leaf nodes and other osg::Group nodes. It is the most commonly-used base class of the various NodeKits—that is, nodes with various functionalities. The osg::Group class derives from osg::Node, and thus indirectly derives from osg::Referenced. The osg::Group class contains a children list with each child node managed by the smart pointer osg::ref_ptr<>. This ensures that there will be no memory leaks whenever deleting a set of cascading nodes in the scene graph. The osg::Group class provides a set of public methods for defining interfaces for handling children. These are very similar to the drawable managing methods of osg::Geode, but most of the input parameters are osg::Node pointers. The public method addChild() attaches a node to the end of the children list. Meanwhile, there is an insertChild() method for inserting nodes to osg::Group at a specific location, which accepts an integer index and a node pointer as parameters. The public methods removeChild() and removeChildren() will remove one or more child nodes from the current osg::Group object. The latter uses two parameters: the zero-based index of the start element, and the number of elements to be removed. The getChild() returns the osg::Node pointer stored at a specified zero-based index. The getNumChildren() returns the total number of children. You will be able to handle the child interface of osg::Group with ease because of your previous experience of handling osg::Geode and drawables. Managing parent nodes We have already learnt that osg::Group is used as the group node, and osg::Geode as the leaf node of a scene graph. Additionally, both classes should have an interface for managing parent nodes. OSG allows a node to have multiple parents. In this section, we will first have a glimpse of parent management methods, which are declared in the osg::Node class directly: The method getParent() returns an osg::Group pointer as the parent node. It requires an integer parameter that indicates the index in the parent's list. The method getNumParents() returns the total number of parents. If the node has a single parent, this method will return 1, and only getParent(0) is available at this time. The method getParentalNodePaths() returns all possible paths from the root node of the scene to the current node (but excluding the current node). It returns a list of osg::NodePath variables. The osg::NodePath is actually a std::vector object of node pointers, for example, assuming we have a graphical scene: The following code snippet will find the only path from the scene root to the node child3: osg::NodePath& nodePath = child3->getParentalNodePaths()[0]; for ( unsigned int i=0; i<nodePath.size(); ++i ) { osg::Node* node = nodePath[i]; // Do something... } You will successively receive the nodes Root, Child1, and Child2 in the loop. We don't need to use the memory management system to reference a node's parents. When a parent node is deleted, it will be automatically removed from its child nodes' records as well. A node without any parents can only be considered as the root node of the scene graph. In that case, the getNumParents() method will return 0 and no parent node can be retrieved. Time for action – adding models to the scene graph In the past examples, we always loaded a single model, like the Cessna, by using the osgDB::readNodeFile() function. This time we will try to import and manage multiple models. Each model will be assigned to a node pointer and then added to a group node. The group node, which is defined as the scene root, is going to be used by the program to render the whole scene graph at last: Include the necessary headers: #include <osg/Group> #include <osgDB/ReadFile> #include <osgViewer/Viewer> In the main function, we will first load two different models and assign them to osg::Node pointers. A loaded model is also a sub-scene graph constructed with group and leaf nodes. The osg::Node class is able to represent any kind of sub graphs, and if necessary, it can be converted to osg::Group or osg::Geode with either the C++ dynamic_cast<> operator, or convenient conversion methods like asGroup() and asGeode(), which are less time-costly than dynamic_cast<>. osg::ref_ptr<osg::Node> model1 = osgDB::readNodeFile( "cessna.osg" ); osg::ref_ptr<osg::Node> model2 = osgDB::readNodeFile( "cow.osg" ); Add the two models to an osg::Group node by using the addChild() method: osg::ref_ptr<osg::Group> root = new osg::Group; root->addChild( model1.get() ); root->addChild( model2.get() ); Initialize and start the viewer: osgViewer::Viewer viewer; viewer.setSceneData( root.get() ); return viewer.run(); Now you will see a cow getting stuck in the Cessna model! It is a little incredible to see that in reality, but in a virtual world, these two models just belong to uncorrelated child nodes managed by a group node, and then rendered separately by the scene viewer. What just happened? Both osg::Group and osg::Geode are derived from the osg::Node base class. The osg::Group class allows the addition of any types of child nodes, including the osg::Group itself. However, the osg::Geode class contains no group or leaf nodes. It only accepts drawables for rendering purposes. It is convenient if we can find out whether the type of a certain node is osg::Group, osg::Geode, or other derived type especially those read from files and managed by ambiguous osg::Node classes, such as: osg::ref_ptr<osg::Node> model = osgDB::readNodeFile( "cessna.osg" ); Both the dynamic_cast<> operator and the conversion methods like asGroup(), asGeode(), among others, will help to convert from one pointer or reference type to another. Firstly, we take the dynamic_cast<> operator as an example. This can be used to perform downcast conversions of the class inheritance hierarchy, such as: osg::ref_ptr<osg::Group> model = dynamic_cast<osg::Group*>( osgDB::readNodeFile("cessna.osg") ); The return value of the osgDB::readNodeFile() function is always osg::Node*, but we can also try to manage it with an osg::Group pointer. If, the root node of the Cessna sub graph is a group node, then the conversion will succeed, otherwise it will fail and the variable model will be NULL.   You may also perform an upcast conversion, which is actually an implicit conversion: osg::ref_ptr<osg::Group> group = ...; osg::Node* node1 = dynamic_cast<osg::Node*>( group.get() ); osg::Node* node2 = group.get(); On most compilers, both node1 and node2 will compile and work fine. The conversion methods will do a similar job. Actually, it is preferable to use those methods instead of dynamic_cast<> if one exists for the type you need, especially in a performance-critical section of code: // Assumes the Cessna's root node is a group node. osg::ref_ptr<osg::Node> model = osgDB::readNodeFile("cessna.osg"); osg::Group* convModel1 = model->asGroup(); // OK! osg::Geode* convModel2 = model->asGeode(); // Returns NULL. Traversing the scene graph A typical traversal consists of the following steps: First, start at an arbitrary node (for example, the root node). Move down (or sometimes up) the scene graph recursively to the child nodes, until a leaf node is reached, or a node with no children is reached. Backtrack to the most recent node that doesn't finish exploring, and repeat the above steps. This can be called a depth-first search of a scene graph. Different updating and rendering operations will be applied to all scene nodes during traversals, which makes traversing a key feature of scene graphs. There are several types of traversals, with different purposes: An event traversal firstly processes mouse and keyboard inputs, and other user events, while traversing the nodes. An update traversal (or application traversal) allows the user application to modify the scene graph, such as setting node and geometry properties, applying node functionalities, executing callbacks, and so on. A cull traversal tests whether a node is within the viewport and worthy of being rendered. It culls invisible and unavailable nodes, and outputs the optimized scene graph to an internal rendering list. A draw traversal (or rendering traversal) issues low-level OpenGL API calls to actually render the scene. Note that it has no correlation with the scene graph, but only works on the rendering list generated by the cull traversal. In the common sense, these traversals should be executed per frame, one after another. But for systems with multiple processors and graphics cards, OSG can process them in parallel and therefore improve the rendering efficiency. The visitor pattern can be used to implement traversals. Transformation nodes The osg::Group nodes do nothing except for traversing down to their children. However, OSG also supports the osg::Transform family of classes, which is created during the traversal-concatenated transformations to be applied to geometry. The osg::Transform class is derived from osg::Group. It can't be instantiated directly. Instead, it provides a set of subclasses for implementing different transformation interfaces. When traversing down the scene graph hierarchy, the osg::Transform node always adds its own transformation to the current transformation matrix, that is, the OpenGL model-view matrix. It is equivalent to concatenating OpenGL matrix commands such as glMultMatrix(), for instance: This example scene graph can be translated into following OpenGL code: glPushMatrix(); glMultMatrix( matrixOfTransform1 ); renderGeode1(); // Assume this will render Geode1 glPushMatrix(); glMultMatrix( matrixOfTransform2 ); renderGeode2(); // Assume this will render Geode2 glPopMatrix(); glPopMatrix(); To describe the procedure using the concept of coordinate frame, we could say that Geode1 and Transform2 are under the relative reference frame of Transform1, and Geode2 is under the relative frame of Transform2. However, OSG also allows the setting of an absolute reference frame instead, which will result in the behavior equivalent to the OpenGL command glLoadMatrix(): transformNode->setReferenceFrame( osg::Transform::ABSOLUTE_RF ); And to switch back to the default coordinate frame: transformNode->setReferenceFrame( osg::Transform::RELATIVE_RF );
Read more
  • 0
  • 0
  • 7609

article-image-yui-2x-using-event-component
Packt
14 Dec 2010
7 min read
Save for later

YUI 2.X: Using Event Component

Packt
14 Dec 2010
7 min read
Yahoo! User Interface Library 2.x Cookbook Over 70 simple incredibly effective recipes for taking control of Yahoo! User Interface Library like a Pro Easily develop feature-rich internet applications to interact with the user using various built-in components of YUI library Simple and powerful recipes explaining how to use and implement YUI 2.x components Gain a thorough understanding of the YUI tools Plenty of example code to help you improve your coding and productivity with the YUI Library Hands-on solutions that take a practical approach to recipes In this article, you will learn how to use YUI to handle JavaScript events, what special events YUI has to improve the functionality of some JavaScript events, and how to write custom events for your own application. Using YUI to attach JavaScript event listeners When attaching events in JavaScript most browsers use the addEventListener function , but the developers of IE use a function called attachEvent. Legacy browsers do not support either function, but instead require developers to attach functions directly to element objects using the 'on' + eventName property (for example myElement.onclick=function(){...}). Additionally, the execution context of event callback functions varies depending on how the event listener is attached. The Event component normalizes all the cross-browser issues, fixes the execution context of the callback function, and provides additional event improvements. This recipe will show how to attach JavaScript event listeners, using YUI. How to do it... Attach a click event to an element: var myElement = YAHOO.util.Dom.get('myElementId'); var fnCallback = function(e) { alert("myElementId was clicked"); }; YAHOO.util.Event.addListener(myElement, 'click', fnCallback); Attach a click event to an element by its ID: var fnCallback = function(e) { alert("myElementId was clicked"); }; YAHOO.util.Event.addListener('myElementId','click',fnCallback) Attach a click event to several elements at once: var ids = ["myElementId1", "myElementId2", "myElementId3"]; var fnCallback = function(e) { var targ = YAHOO.util.Event.getTarget(e); alert(targ.id + " was clicked"); }; YAHOO.util.Event.addListener(ids, 'click', fnCallback); When attaching event listeners, you can provide an object as the optional fourth argument, to be passed through as the second argument to the callback function: var myElem = YAHOO.util.Dom.get('myElementId'); var fnCallback = function(e, obj) { alert(obj); }; var obj = "I was passed through."; YAHOO.util.Event.addListener(myElem,'click',fnCallback,obj); When attaching event listeners, you can change the execution context of the callback function to the fourth argument, by passing true as the optional fifth argument: var myElement = YAHOO.util.Dom.get('myElementId'); var fnCallback = function(e) { alert('My execution context was changed.'); }; var ctx = { /* some object to be the execution context of callback */ }; YAHOO.util.Event.addListener( myElement, 'click', fnCallback, ctx, true); How it works... The addListener function wraps the native event handling functions, normalizing the cross- browser differences. When attaching events, YUI calls the correct browser specific function, or defaults to legacy event handlers. Before executing the callback function, the Event component must (in some browsers) find the event object and adjust the execution context of the callback function. The callback function is normalized by wrapping it in a closure function that executes when the browser event fires, thereby allowing YUI to correct the event, before actually executing the callback function. In legacy browsers, which can only have one callback function per event type, YUI attaches a callback function that iterates through the listeners attached by the addListener function There's more... The addListener function returns true if the event listener is attached successfully and false otherwise. If the element to listen on is not available when the addListener function is called, the function will poll the DOM and wait to attach the listener when the element becomes available. Additionally, the Event component also keeps a list of all events that it has attached. This list is maintained to simplify removing events listeners, and so that all event listeners can be removed when the end-user leaves the page. Find all events attached to an element: var listeners = YAHOO.util.Event.getListeners('myElementId'); for (var i=0,j=listeners.length; i<j; i+=1) { var listener = listeners[i]; alert(listener.type); // event type alert(listener.fn); // callback function alert(listener.obj); // second argument of callback alert(listener.adjust); // execution context } Find all events of a certain type attached to an element: // only click listeners var listeners = YAHOO.util.Event.getListeners('myElementId', 'click'); The garbage collector in JavaScript does not always do a good job cleaning up event handlers. When removing nodes from the DOM, remember to remove events you may have added as well. More on YAHOO.util.Event.addListener The addListener function has been aliased by the shorter on function: var myElement = YAHOO.util.Dom.get('myElementId'); var fnCallback = function(e) { alert("myElementId was clicked"); }; YAHOO.util.Event.on(myElement, 'click', fnCallback); By passing an object in as the optional fifth argument of addListener, instead of a Boolean, you can change the execution context of the callback to that object, while still passing in an another object as the optional fourth argument: var myElement = YAHOO.util.Dom.get('myElementId'); var fnCallback = function(e, obj) { // this executes in the context of 'ctx' alert(obj); }; var obj = "I was passed through."; var ctx = { /* some object to be the execution context of callback */ }; YAHOO.util.Event.addListener( myElement,'click',fnCallback,obj, ctx); Lastly, there is an optional Boolean value that can be provided as the sixth argument of addListener, which causes the callback to execute in the event capture phase, instead of the event bubbling phase. You probably won't ever need to set this value to true, but if you want to learn more about JavaScript event phases see: http://www.quirksmode.org/js/events_order.html Event normalization functions The event object, provided as the first argument of the callback function, contains a variety of values that you may need to use (such as the target element, character code, etc.). YUI provides a collection of static functions that normalizes the cross-browser variations of these values. Before trying to use these properties, you should read this recipe, as it walks you through each of those functions. How to do it... Fetch the normalized target element of an event: var fnCallback = function(e) { var targetElement = YAHOO.util.Event.getTarget(e); alert(targetElement.id); }; YAHOO.util.Event.on('myElementId', 'click', fnCallback); Fetch the character code of a key event (also known as the key code): var fnCallback = function(e) { var charCode = YAHOO.util.Event.getCharCode(e); alert(charCode); }; YAHOO.util.Event.on('myElementId', 'keypress', fnCallback); Fetch the x and y coordinates of a mouse event: var fnCallback = function(e) { var x = YAHOO.util.Event.getPageX(e); var y = YAHOO.util.Event.getPageY(e); alert("x-position=" + x + " and x-position= " + y); }; YAHOO.util.Event.on('myElementId', 'click', fnCallback); Fetch both the x and y coordinates of a mouse event, using: var fnCallback = function(e) { var point = YAHOO.util.Event.getXY(e); alert("x-position="+point[0]+" and x-position= "+point[1]); }; YAHOO.util.Event.on('myElementId', 'click', fnCallback); Fetch the normalized related target element of an event: var fnCallback = function(e) { var targetElement = YAHOO.util.Event.getRelatedTarget(e); alert(targetElement.id); }; YAHOO.util.Event.on('myElementId', 'click', fnCallback); Fetch the normalized time of an event: var fnCallback = function(e) { var time = YAHOO.util.Event.getTime(e); alert(time); }; YAHOO.util.Event.on('myElementId', 'click', fnCallback); Stop the default behavior, propagation (bubbling) of an event, or both: var fnCallback = function(e) { // prevents the event from bubbling up to ancestors YAHOO.util.Event.stopPropagation(e); // prevents the event's default YAHOO.util.Event.preventDefault(e); // prevents the event's default behavior and bubbling YAHOO.util.Event.stopEvent(e); }; YAHOO.util.Event.on('myElementId', 'click', fnCallback); How it works... All of these functions test to see if a value exists on the event for each cross-browser variation of a property. The functions then normalize those values and return them. The stopPropogation and preventDefault functions actually modify the equivalent cross-browser property of the event, and delegate the behavior to the browsers.
Read more
  • 0
  • 0
  • 3243
article-image-ssis-applications-using-sql-azure
Packt
14 Dec 2010
5 min read
Save for later

SSIS Applications using SQL Azure

Packt
14 Dec 2010
5 min read
Microsoft SQL Azure Enterprise Application Development Build enterprise-ready applications and projects with SQL Azure Develop large scale enterprise applications using Microsoft SQL Azure Understand how to use the various third party programs such as DB Artisan, RedGate, ToadSoft etc developed for SQL Azure Master the exhaustive Data migration and Data Synchronization aspects of SQL Azure. Includes SQL Azure projects in incubation and more recent developments including all 2010 updates SSIS and SSRS are not presently supported on SQL Azure. However, this is one of the future enhancements that will be implemented. While they are not supported on Windows Azure platform, they can be used to carry out both data integration and data reporting activities. Moving a MySQL database to SQL Azure database Realizing the growing importance of MySQL and PHP from the LAMP stack, Microsoft has started providing programs to interact with and leverage these programs. For example, the SSMA described previously and third-party language hook ups to Windows Azure are just the beginning. For small businesses who are now using MySQL and who might be contemplating to move to SQL Azure, migration of data becomes important. In the following section, we develop a SQL Server Integration Services package, which when executed transfers a table from MySQL to SQL Azure. Creating the package The package consists of a dataflow task that extracts table data from MySQL (source) and transfers it to SQL Azure (destination). The dataflow task consists of an ADO.NET Source connecting to MySQL and an ADO.NET Destination connecting to SQL Azure. In the next section, the method for creating the two connections is explained. Creating the source and destination connections In order to create the package we need a connection to MySQL and a connection to SQL Azure. We use the ADO.NET Source and ADO.NET Destination for the flow of the data. In order to create an ADO.NET Source connection to MySQL we need to create an ODBC DSN as we will be using the .NET ODBC Data Provider. Details of creating an ODBC DSN for the version of MySQL are described here: http://www.packtpub.com/article/mysql-linked-server-on-sql-server-2008. Configuring a Connection Manager for MySQL is described here: http://www.packtpub.com/article/mysql-data-transfer-using-sql-server-integration-servicesssis. The Connection Manager for SQL Azure Destination uses a .NET SQLClient Data Provider and this is described here (when SQL Azure was in CTP but no change is required for the RTM): . The authentication information needs to be substituted for the current SQL Azure database. Note that these procedures are not repeated step-by-step as they are described in great detail in the referenced links. However some key features of the configuration details are presented here: The ODBC DSN created is shown here with the details: The settings used for the MySQL Connection Manager are the following:Provider: .NET ProvidersOdbc Data Provider Data Source Specification Use user or system data source name: MySqlData Login Information: root Password: <root password> The settings for the SQL Azure are the following:Provider: .Net ProvidersSQLClient Data Provider Server name: xxxxxxx.database.windows.net Log on to the server Use SQL Server authentication User name: mysorian Password: ******** Connect to a database Select or enter database name: Bluesky (if authentication is correct, it should appear in the drop-down) Creating the package We begin with the source connection and after configuring the Connection Manager, by editing the source as shown in the following screenshot. You may notice that the SQL command is used rather than the name of the table. It was found however, that choosing the name of the table results in an error. Probably a bug, and as a workaround we use the SQL command. With this you can preview the data and verify it. After verifying the data from the source, drag-and-drop the green dangling line from the source to the ADO.NET Destination component connected to SQL Azure. Double-clicking the destination component brings up the ADO.NET Destination Editor with the following details: Connection manager: XXXXXXXXX.database.windows.net.Bluesky.mysorian2 Use a table or view: "dbo"."AzureEmployees" Use Bulk Insert when possible: checked There will be a warning message at the bottom of screen: Map the columns on the Mappings page. The ADO.NET Destination Editor window comes up with a list of tables or views displaying one of the tables. We will be creating a new table. Clicking New… button for the field Use a table or view brings up the Create Table window with a default create table statement with all the columns from the source table and a default table name, ADO.NET Destination. Modify the create table statement as follows: CREATE TABLE fromMySql( "Id" int Primary Key Clustered, "Month" nvarchar(11), "Temperature" float, "RecordHigh" float When you click on OK in this screen you will have completed the configuration of the destination. There are several things you can add to make troubleshooting easier by adding Data Viewers, error handling, and so on. These are omitted here but best practices require that these should be in place when you design packages. The completed destination component should display the following details: Connection manager: XXXXXXX.database.windows.net.Bluesky.mysorian2 Use a table or view: fromMySql Use Bulk Insert when possible: Checked The columns from the source are all mapped to the columns of the destination, which can be verified in the Mappings page, as shown in the following screenshot: When the source and destination are completely configured as described here you can build the project from the main menu. When you execute the project, the program starts running and after a while both the components turn yellow and then go green indicating that the package has executed successfully. The rows (number) that are written to the destination also appear in the designer. You may now log on to SQL Azure in SSMS and verify that the table fromMySql2 has been created and that 12 rows of data from MySQL's data have been written into it.
Read more
  • 0
  • 0
  • 2253

article-image-page-management-part-one-cms-design
Packt
14 Dec 2010
6 min read
Save for later

Page Management - Part One in CMS Design

Packt
14 Dec 2010
6 min read
  CMS Design Using PHP and jQuery Build and improve your in-house PHP CMS by enhancing it with jQuery Create a completely functional and a professional looking CMS Add a modular architecture to your CMS and create template-driven web designs Use jQuery plugins to enhance the "feel" of your CMS A step-by-step explanatory tutorial to get your hands dirty in building your own CMS         Read more about this book       (For more resources on this subject, see here.) How pages work in a CMS A "page" is simply the main content which should be shown when a certain URL is requested. In a non-CMS website, this is easy to see, as a single URL returns a distinct HTML file. In a CMS though, the page is generated dynamically, and may include features such as plugins, different views depending on whether the reader was searching for something, whether pagination is used, and other little complexities. In most websites, a page is easily identified as the large content area in the middle (this is an over-simplification). In others, it's harder to tell, as the onscreen page may be composed of content snippets from other parts of the site. We handle these differences by using page "types", each of which can be rendered differently on the frontend. Examples of types include gallery pages, forms, news contents, search results, and so on. In this article, we will create the simplest type, which we will call "normal". This consists of a content-entry textarea in the admin area, and direct output of that content on the front-end. You could call this "default" if you want, but since a CMS is not always used by people from a technical background, it makes sense to use a word that they are more likely to recognize. I have been asked before by clients what "default" means, but I've never been asked what "normal" means. At the very least, a CMS needs some way to create the simplest of web pages. This is why the "normal" type is not a plugin, but is built into the core. Listing pages in the admin area To begin, we will add Pages to the admin menu. Edit /ww.admin/header.php and add the following highlighted line: <ul> <li><a href="/ww.admin/pages.php">Pages</a></li> <li><a href="/ww.admin/users.php">Users</a></li> And one more thing—when we log into the administration part of the CMS, it makes sense to have the "front page" of the admin area be the Pages section. After all, most of the work in a CMS is done in the Pages section. So, we change /ww.admin/index.php so it is a synonym for /ww.admin/pages.php. Replace the /ww.admin/index.php file with this: <?php require 'pages.php'; Next, let's get started on the Pages section. First, we will create /ww.admin/pages.php: <?php require 'header.php'; echo '<h1>Pages</h1>'; // { load menu echo '<div class="left-menu">'; require 'pages/menu.php'; echo '</div>'; // } // { load main page echo '<div class="has-left-menu">'; require 'pages/forms.php'; echo '</div>'; // } echo '<style type="text/css"> @import "pages/css.css";</style>'; require 'footer.php'; Notice how I've commented blocks of code, using // { to open the comment at the beginning of the block, and // } at the end of the block. This is done because a number of text editors have a feature called "folding", which allows blocks enclosed within delimiters such as { and } to be hidden from view, with just the first line showing. For instance, the previous code example looks like this in my Vim editor: What the page.php does is to load the headers, load the menu and page form, and then load the footers. For now, create the directory /ww.admin/pages and create a file in it called /ww.admin/pages/forms.php: <h2>FORM GOES HERE</h2> And now we can create the page menu. Use the following code to create the file /ww.admin/pages/menu.php: <?php echo '<div id="pages-wrapper">'; $rs=dbAll('select id,type,name,parent from pages order by ord,name'); $pages=array(); foreach($rs as $r){ if(!isset($pages[$r['parent']]))$pages[$r['parent']]=array(); $pages[$r['parent']][]=$r; } function show_pages($id,$pages){ if(!isset($pages[$id]))return; echo '<ul>'; foreach($pages[$id] as $page){ echo '<li id="page_'.$page['id'].'">' .'<a href="pages.php?id='.$page['id'].'"'>' .'<ins>&nbsp;</ins>'.htmlspecialchars($page['name']) .'</a>'; show_pages($page['id'],$pages); echo '</li>'; } echo '</ul>'; } show_pages(0,$pages); echo '</div>'; That will build up a &ltul> tree of pages. Note the use of the "parent" field in there. Most websites follow a hierarchical "parent-child" method of arranging pages, with all pages being a child of either another page, or the "root" of the site. The parent field is filled with the ID of the page within which it is situated. There are two main ways to indicate which page is the "front" page (that is, what page is shown when someone loads up http://cms/ with no page name indicated). You can have one single page in the database which has a parent of 0, meaning that it has no parent—this page is what is looked for when http://cms/ is called. In this scheme, pages such as http://cms/pagename have their parent field set to the ID of the one page which has a parent of 0. You can have many pages which have 0 as their parent, and each of these is said to be a "top-level" page. One page in the database has a flag set in the special field which indicates that this is the front page. In this scheme, pages named like http://cms/pagename all have a parent of 0, and the page corresponding to http://cms/ can be located anywhere at all in the database. Case 1 has a disadvantage, in that if you want to change what page is the front page, you need to move the current page under another one (or delete it), then move all the current page's child-pages so they have the new front page's ID as a parent, and this can get messy if the new front-page already had some sub-pages—especially if there are any with equal names. Case 2 is a much better choice because you can change the front page whenever you want, and it doesn't cause any problems at all. When you view the site in your browser now, it looks like this:
Read more
  • 0
  • 0
  • 2608

article-image-drupal-7-module-development-drupals-theme-layer
Packt
14 Dec 2010
16 min read
Save for later

Drupal 7 Module Development: Drupal's Theme Layer

Packt
14 Dec 2010
16 min read
  Drupal 7 Module Development Create your own Drupal 7 modules from scratch Specifically written for Drupal 7 development Write your own Drupal modules, themes, and libraries Discover the powerful new tools introduced in Drupal 7 Learn the programming secrets of six experienced Drupal developers Get practical with this book's project-based format Business logic versus presentation logic So what would be the best way to get our data and functionality marked up? Do we simply wrap each piece of data in HTML and return the whole as a giant string? Like the following example: return '<div class="wrapper">' . $data . '</div>'; Fortunately, we don't. Like all other well-designed applications, Drupal separates its business logic from its presentation logic. Traditionally, the primary motivations for this separation of concerns are as follows: To make the code easier to maintain. To make it possible to easily swap out one layer's implementation without having to re-write the other layers. As we shall see, Drupal takes the "swap-ability" aspect to the extreme. As we mentioned in the introduction of this article, the default theme selected on the Appearance page is the most obvious part of the theme layer. Also, you might think that the theme is responsible for applying the HTML and CSS for the website. However, there are thousands of contributed modules on drupal.org. Should the theme be responsible for marking up all of those modules' data? Obviously not. Since a module is most intimately familiar with its own data and functionality, it's the module's responsibility to provide the default theme implementation. As long as the module uses the theme system properly, a theme will be able to override any HTML and CSS by hot-swapping its own implementation for the module's implementation. After the data has been retrieved and manipulated in the heart of your module (the business logic), it will need to provide the default theme implementation. Sometimes a particular theme will need to override your implementation in order for it to achieve a specifc design goal; if the theme provides its own implementation, Drupal will use the theme implementation instead of the module's default implementation. $variables = array('items' => $list, 'type' => 'ol'); $content = theme('item_list', $variables); By calling the theme() function, we are delegating the responsibility of determining and using the proper theme implementation. We're saying: "Hey, theme()! I want to markup my data as an item_list. Can you do that for me? I don't need to know the details. kthxbye." Our module just needs to decide which theme hook it wants to use to markup its data. Should the data be displayed in an unordered list, a table, or a wordle? Hook crazy? In addition to API hooks, Drupal also has theme hooks. A theme hook is simply the name of a particular way to markup some data. For example, passing data to the item_list theme hook will result in different markup then passing data to the links theme hook. However, while normally every module's hook function will be called when Drupal invokes an API hook, only one theme hook implementation will be invoked when Drupal invokes a theme hook. There are actually two different ways you can make an implementation (which we will discuss later), but for now we'll only talk about the simplest method for module developers—theme functions. When you call theme(), it will look for a default theme function named theme_HOOKNAME and for an optional theme override function called THEMENAME_HOOKNAME. If you dig into Drupal's internals, you'll fnd a theme_item_list() inside includes.inc or theme.inc. This is Drupal's default theme implementation for an item_list. If our active theme was Bartik, and if Bartik implemented a theme override called bartik_item_list(), then theme() would use the Bartik theme's implementation instead of the default one. The preceding fgure shows one piece of data as it passes through a module and a theme. However, in order for you to understand the full power of Drupal's theme layer, you also need to understand how the entire page is built. However, since all of the active theme's modifcations occur after any module modifcations, from a module developer's perspective, all of this theme inheritance is transparent. Since modules don't need to know anything about the structure o the theme and its ancestry, we'll simply talk about "the theme" in this book. Just be aware that the actual theme may be more complex. Base themes and sub-themes If you've previously read anything about Drupal theming, you've probably heard about base themes and sub-themes. Any theme can declare a parent theme in its .info file using the base theme key and it will inherit all the hook implementations from its parent theme (and its parent's parent theme, and so on). Data granularity One of the things that makes Drupal theming so powerful is its granularity. Each piece of content is handled separately as it's passed through the theming system. Each bit of data is themed individually, then combined into ever-larger chunks. At each step in the aggregation process, it's themed again. The following illustration will make this clearer: As you can see in the preceding illustration, for a typical blog post, each comment is pulled from the database and sent through the theme system to get HTML markup added to it. Then all the comments are aggregated together into a "comment wrapper" where additional markup and, usually, a "new comment" form is added. Then the single group of comments is passed to the node theming where it is combined with other pieces of the blog post's content. This process of theming bits of content, aggregation, and theming again is repeated until we've built the entire HTML page ready to be sent to a web browser. There are two advantages to this granular system. First, since each module is responsible for theming its own data, it can either create a very specialized theme hook for its data or it can re-use an existing theme hook. Re-using a theme hook ensures a consistent set of markup for similar data structures while still allowing customized CSS classes (Most theme hooks allow custom classes to be passed as parameters.) For example, the list of links after a node (read more, add new comment, and so on) re-uses the links theme hook, and the links after each comment use the same links theme hook. The second advantage is for the theme developer. Having a fine-grained theming system means that a theme, if it chooses to, can literally rewrite all of the markup for its own design purposes. As module developers we need to be keenly aware of the themer's desire to have granular theming overrides.   Theme engines Some themes require alternate theme engines. Theme engines can provide alternate template syntax, naming standards, and helper functions. Several theme engines are available for download at http://drupal.org/project/theme+engines. However, we won't be discussing any theme engines except for Drupal's default theme engine, PHPTemplate. The PHPTemplate theme engine has been the default theme since Drupal 4.7, has been continuously improved with each version, and has proven its worth again and again. Over 99% of themes available for download on drupal.org use the default PHPTemplate theme engine. Two ways to theme So now that we have a good understanding of higher level concepts, let's get down to the nitty-gritty of theme implementations. There are actually two different ways to implement a theme hook: Theme functions: pass data to a PHP function to wrap it in markup Templates: pass data to a template which is a PHP file mixed with markup and PHP print statements Let's look at each of these in turn. Theme functions For a module developer, the easiest type of implementation to understand is a theme function. Theme functions just need to follow a few simple rules in order for them to work properly. First, the name of the theme function follows the pattern: theme_[theme hook name] Since the theme hook name is used directly in the theme function's name, theme hook names have the same constraints on naming as regular PHP function names; the only valid characters in theme hook names are alphanumeric characters and underscores. So if a module has created an example_format theme hook, it would implement it with theme function named theme_example_format(). Second, the theme function will only have a single parameter, as follows: function theme_THEME_HOOK_NAME($variables) {…} The theme function variables are an associative array containing the pieces of data we wish to markup and any options we want to pass to the function. It may seem extremely odd not to use multiple parameters and PHP's ability to specify default values for each parameter. In fact, previous versions of Drupal did use multiple parameters. We'll see why Drupal now only uses one parameter in just a moment when we talk about preprocess functions. For an example of a $variables array, let's look at how the DocBlock of the theme_item_list() function defnes it: Items: An array of items to be displayed in the list. If an item is a string, then it is used as is. If an item is an array, then the data element of the array is used as the contents of the list item. If an item is an array with a "children" element, those children are displayed in a nested list. All other elements are treated as attributes of the list item element. Title: The title of the list. Type: The type of list to return (e.g. ul,ol). Attributes: The attributes applied to the list element. The items and title keys hold the actual data, and the type and attributes keys are options that specify how to build the item list. Third, the theme function should return a string that contains the rendered representation of the data. This is usually a string of HTML, but some theme hooks return other types of themed markup. For example, theme_syslog_format returns a simple string with pipe-separated data values for use in a *NIX syslog error log. That's it! As you can see, theme functions have very simple requirements and in every other way are standard PHP functions. The major difference between most functions and theme functions is that you should never call theme functions directly. It may be tempting to take your data and call theme_item_list($vars) directly, but you should instead call theme("item_list", $vars). This method of calling theme functions indirectly ensures that themes are able to override any module's default theme function (or template). It also allows the theme() function to work additional magic, including allowing other modules to alter the theme function's variables before they are used. Preprocess functions Now we're starting to see the real flexibility of the theme system. Preprocess functions allow one module to alter the variables used by another module when it calls a theme hook. So if some code passes data to theme() for a particular theme hook, preprocess functions will be called to alter the data before the actual theme hook implementation is called. The following steps are carried out: Code calls theme('hook_name', $variables). theme() calls preprocess functions for hook_name. Preprocess functions modify variables. theme() calls actual implementation for hook_name with modifed variables. All preprocess functions take the form of: [module]_preprocess_[theme hook name](&$variables) So if the foo module wants to alter the variables for the item_list theme hook, it could define the function as follows: function foo_preprocess_item_list(&$variables) { // Add a class to the list wrapper. $variables['attributes']['class'][] = 'foo-list'; } Notice that the $variables parameter is defned with an ampersand in front of it. That's PHP notation to pass the parameter by reference. Instead of getting a copy of the variables, the foo_preprocess_item_list() function will get access to the actual $variables which is later passed to the theme function implementation. So any modifications that the preprocess function makes to the $variables parameter will be preserved when those variables are passed to the theme function. That's the reason our example foo_preprocess_item_list() function doesn't return anything; its work is done directly on the original $variables. This is extremely handy for module developers as it allows all sorts of integration with other modules. Since the variables parameter is a mix of data and options, modules can alter both the raw data and change the way data will be rendered. This can be as simple as one module needing a special class for use in its JavaScript code and adding that class to another module's themed content by appending to the $variables['attributes']['class'] array, or can be more complex interactions like the i18n module translating the language used in blocks. Imagine we've built a retro module that integrates GeoCities and we want to replace all links to a user's profle page with a link to the user's GeoCities homepage. We can do that relatively easily with a preprocess function. First let's look at the following theme_username function's documentation: /** * Format a username. * * @param $variables * An associative array containing: * - account: The user object to format. * - name: The user's name, sanitized. * - extra: Additional text to append to the user's name, sanitized. * - link_path: The path or URL of the user's profile page, home * page, or other desired page to link to for more information * about the user. * - link_options: An array of options to pass to the l() function's * $options parameter if linking the user's name to the user's * page. * - attributes_array: An array of attributes to pass to the * drupal_attributes() function if not linking to the user's page. */ Quite conveniently, theme_username() has a handy $link_path variable that we want to alter to achieve our old-school giggles. Assuming that we've used some other business logic with the user module's hooks to load our GeoCities URL into the user's account (the "hard" part), replacing the link to the user's profle page can be accomplished with the following simple preprocess function: /** * Implements awesomeness with hook_preprocess_username(). */ function retro_preprocess_username(&$variables) { $variables['link_path'] = $variables['account']->geocities_url; } That's it! We don't have to override the user module's theme implementation; we just modify its parameters. Theme overrides While module developers usually don't have to worry about whether a theme overrides a particular theme function or not, it's still important to understand how this mechanism works. Drupal theme is normally composed of CSS, images, JavaScripts, template files (discussed shortly), a .info file, and a template.php file. The template.php file is analogous to a module's .module file. It contains all of the PHP functions for the theme and is automatically loaded when the theme is initialized. If a theme wants to override a particular theme function, it needs to copy the theme function from its original location and paste it into its template.php file. Then it needs to change the function's prefix from theme to its own name and finally, it needs to start making the desired changes to the function. For example, if the Bartik theme wants to override the theme_menu_local_tasks() function in order to add some markup around the page's tabs, it would copy the entire function from includes/menu.inc, paste it into Bartik's template.php, and rename it to bartik_menu_local_tasks(). Fortunately, when a theme overrides a default theme function, a module's preprocess functions continue to work as normal. Themes also have the ability to create preprocess functions. If the Bartik theme decides to format a user's name in "last name, frst name" format, it can implement a bartik_preprocess_username() function. Fortunately, a theme's preprocess functions do not override a module's preprocess functions. All preprocess functions are run; frst any module's preprocess functions and then the theme's preprocess function. Template files While theme functions might be the easiest for module developers to understand, template files are the easiest for themes to grasp. When a theme hook is implemented with template files, they are used instead of theme functions. However, from a module developer's standpoint, there is actually a remarkable amount of similarity between template files and theme functions. First, let's take a closer look at template files. Templates are files primarily containing HTML but with some PHP statements mixed in using the template's variables. Instead of declaring a theme_hook_name() function, a module would instead create a hook-name.tpl.php file. The following are the contents of a typical template file, typical-hook.tpl.php: <div class="<?php print $classes; ?>"<?php print $attributes; ?>> <?php if ($title): ?> <h2<?php print $title_attributes; ?>> <?php print $title; ?> </h2> <?php endif;?> <div class="submitted"> <?php print t('By !author @time ago', array( '@time' => $time, '!author' => $author, )); ?> </div> <div class="content"<?php print $content_attributes; ?>> <?php // We hide the links now so that we can render them later. hide($content['links']); print render($content); ?> </div> <?php print render($content['links']); ?> </div> The preceding example shows the full gamut of the things that you are likely see in a template file. They are as follows: Printing a variable containing a string? Printing a translatable string using t() Conditional if/else/endif statement Delaying rendering on part of a render element with hide() Printing a render element All of the PHP in a template should be limited to printing out variables. This limited amount of PHP makes it much easier for non-programmers to learn how to use template fles compared to theme functions. However, for module developers, the template implementation is still very similar to the theme function implementation; the handful of differences are relatively minor. As with theme function implementations, our module would still need to invoke the theme hook using theme(). $variables = array('typical' => $typical_object); $output = theme('typical_hook', $variables); The theme() function would discover that the typical_hook theme hook was implemented as a template and render the corresponding typical-hook.tpl.php file. The only valid characters in theme hook names are alphanumeric characters and underscores. This is true of all theme hooks, regardless of whether they are implemented as a theme function or as a template file. However, when theme() looks for template implementations, it will automatically convert any underscores in the theme hook name into hyphens while searching for the template file. For example, calling theme('user_picture', $variables) will result in the template file named user-picture.tpl.php being rendered. Also, just like theme functions, other modules can modify the variables using preprocess functions. In template fles the focus is on printing out variables in various places in the markup. So for template fles, the preprocess function takes on a more important role. The only difference between a theme function's preprocess functions and a template file's are the number and type of preprocess functions.
Read more
  • 0
  • 0
  • 4059
article-image-creating-your-first-module-drupal-7-module-development
Packt
14 Dec 2010
15 min read
Save for later

Using Drupal 7 for Module Development

Packt
14 Dec 2010
15 min read
  Drupal 7 Module Development Create your own Drupal 7 modules from scratch Specifically written for Drupal 7 development Write your own Drupal modules, themes, and libraries Discover the powerful new tools introduced in Drupal 7 Learn the programming secrets of six experienced Drupal developers Get practical with this book's project-based format         Read more about this book       The focus of this article by Matt Butcher, author of Drupal 7 Module Development, is module creation. We are going to begin coding in this article. Here are some of the important topics that we will cover in this article: Starting a new module Creating .info files to provide Drupal with module information Creating .module files to store Drupal code Adding new blocks using the Block Subsystem Using common Drupal functions Formatting code according to the Drupal coding standards (For more resources on this subject, see here.) Our goal: a module with a block In this article we are going to build a simple module. The module will use the Block Subsystem to add a new custom block. The block that we add will simply display a list of all of the currently enabled modules on our Drupal installation. We are going to divide this task of building a new module into the three parts: Create a new module folder and module files Work with the Block Subsystem Write automated tests using the SimpleTest framework included in Drupal We are going to proceed in that order for the sake of simplicity. One might object that, following agile development processes, we ought to begin by writing our tests. This approach is called Test-driven Development (TDD), and is a justly popular methodology. Agile software development is a particular methodology designed to help teams of developers effectively and efficiently build software. While Drupal itself has not been developed using an agile process, it does facilitate many of the agile practices. To learn more about agile, visit http://agilemanifesto.org/ However, our goal here is not to exemplify a particular methodology, but to discover how to write modules. It is easier to learn module development by first writing the module, and then learn how to write unit tests. It is easier for two reasons: SimpleTest (in spite of its name) is the least simple part of this article. It will have double the code-weight of our actual module. We will need to become acquainted with the APIs we are going to use in development before we attempt to write tests that assume knowledge of those APIs. In regular module development, though, you may certainly choose to follow the TDD approach of writing tests first, and then writing the module. Let's now move on to the first step of creating a new module. Creating a new module Creating Drupal modules is easy. How easy? Easy enough that over 5,000 modules have been developed, and many Drupal developers are even PHP novices! In fact, the code in this article is an illustration of how easy module coding can be. We are going to create our first module with only one directory and two small files. Module names It goes without saying that building a new module requires naming the module. However, there is one minor ambiguity that ought to be cleared up at the outset, a Drupal module has two names: A human-readable name: This name is designed to be read by humans, and should be one or a couple of words long. The words should be capitalized and separated by spaces. For example, one of the most popular Drupal modules has the human-readable name Views. A less-popular (but perhaps more creatively named) Drupal 6 module has the human-readable name Eldorado Superfly. A machine-readable name: This name is used internally by Drupal. It can be composed of lower-case and upper-case letters, digits, and the underscore character (using upper-case letters in machine names is frowned upon, though). No other characters are allowed. The machine names of the above two modules are views and eldorado_superfly, respectively. By convention, the two names ought to be as similar as possible. Spaces should be replaced by underscores. Upper-case letters should generally be changed to lower-case. Because of the convention of similar naming, the two names can usually be used interchangeably, and most of the time it is not necessary to specifically declare which of the two names we are referring to. In cases where the difference needs to be made (as in the next section), the authors will be careful to make it. Where does our module go? One of the less intuitive aspects of Drupal development is the filesystem layout. Where do we put a new module? The obvious answer would be to put it in the /modules directory alongside all of the core modules. As obvious as this may seem, the /modules folder is not the right place for your modules. In fact, you should never change anything in that directory. It is reserved for core Drupal modules only, and will be overwritten during upgrades. The second, far less obvious place to put modules is in /sites/all/modules. This is the location where all unmodified add-on modules ought to go, and tools like Drush ( a Drupal command line tool) will download modules to this directory. In some sense, it is okay to put modules here. They will not be automatically overwritten during core upgrades. However, as of this writing, /sites/all/modules is not the recommended place to put custom modules unless you are running a multi-site configuration and the custom module needs to be accessible on all sites. The current recommendation is to put custom modules in the /sites/default/modules directory, which does not exist by default. This has a few advantages. One is that standard add-on modules are stored elsewhere, and this separation makes it easier for us to find our own code without sorting through clutter. There are other benefits (such as the loading order of module directories), but none will have a direct impact on us. We will always be putting our custom modules in /sites/default/modules. This follows Drupal best practices, and also makes it easy to find our modules as opposed to all of the other add-on modules. The one disadvantage of storing all custom modules in /sites/default/modules appears only under a specific set of circumstances. If you have Drupal configured to serve multiple sites off of one single instance, then the /sites/default folder is only used for the default site. What this means, in practice, is that modules stored there will not be loaded at all for other sites. In such cases, it is generally advised to move your custom modules into /sites/all/modules/custom. Other module directories Drupal does look in a few other places for modules. However, those places are reserved for special purposes. Creating the module directory Now that we know that our modules should go in /sites/default/modules, we can create a new module there. Modules can be organized in a variety of ways, but the best practice is to create a module directory in /sites/default/modules, and then place at least two files inside the directory: a .info (pronounced "dot-info") file and a .module ("dot-module") file. The directory should be named with the machine-readable name of the module. Similarly, both the .info and .module files should use the machine-readable name. We are going to name our first module with the machine-readable name first, since it is our first module. Thus, we will create a new directory, /sites/default/modules/first, and then create a first.info file and a first.module file: Those are the only files we will need for our module. For permissions, make sure that your webserver can read both the .info and .module files. It should not be able to write to either file, though. In some sense, the only file absolutely necessary for a module is the .info file located at a proper place in the system. However, since the .info file simply provides information about the module, no interesting module can be built with just this file. Next, we will write the contents of the .info file. Writing the .info file The purpose of the .info file is to provide Drupal with information about a module—information such as the human-readable name, what other modules this module requires, and what code files this module provides. A .info file is a plain text file in a format similar to the standard INI configuration file. A directive in the .info file is composed of a name, and equal sign, and a value: name = value By Drupal's coding conventions, there should always be one space on each side of the equals sign. Some directives use an array-like syntax to declare that one name has multiple values. The array-like format looks like this: name[] = value1 name[] = value2 Note that there is no blank space between the opening square bracket and the closing square bracket. If a value spans more than one line, it should be enclosed in quotation marks. Any line that begins with a ; (semi-colon) is treated as a comment, and is ignored by the Drupal INI parser. Drupal does not support INI-style section headers such as those found in the php.ini file. To begin, let's take a look at a complete first.info file for our first module: ;$Id$ name = First description = A first module. package = Drupal 7 Development core = 7.x files[] = first.module ;dependencies[] = autoload ;php = 5.2 This ten-line file is about as complex as a module's .info file ever gets. The first line is a standard. Every .info file should begin with ;$Id$. What is this? It is the placeholder for the version control system to store information about the file. When the file is checked into Drupal's CVS repository, the line will be automatically expanded to something like this: ;$Id: first.info,v 1.1 2009/03/18 20:27:12 mbutcher Exp $ This information indicates when the file was last checked into CVS, and who checked it in. CVS is going away, and so is $Id$. While Drupal has been developed in CVS from the early days through Drupal 7, it is now being migrated to a Git repository. Git does not use $Id$, so it is likely that between the release of Drupal 7 and the release of Drupal 8, $Id$ tags will be removed. You will see all PHP and .info files beginning with the $Id$ marker. Once Drupal uses Git, those tags may go away. The next couple of lines of interest in first.info are these: name = First description = A first module. package = Drupal 7 Development The first two are required in every .info file. The name directive is used to declare what the module's human-readable name is. The description provides a one or two-sentence description of what this module provides or is used for. Among other places, this information is displayed on the module configuration section of the administration interface in Modules. In the screenshot, the values of the name and description fields are displayed in their respective columns. The third item, package, identifies which family (package) of modules this module is related to. Core modules, for example, all have the package Core. In the screenshot above, you can see the grouping package Core in the upper-left corner. Our module will be grouped under the package Drupal 7 Development to represent its relationship. As you may notice, package names are written as human-readable values. When choosing a human-readable module name, remember to adhere to the specifications mentioned earlier in this section. The next directive is the core directive: core = 7.x. This simply declares which main-line version of Drupal is required by the module. All Drupal 7 modules will have the line core = 7.x. Along with the core version, a .info file can also specify what version of PHP it requires. By default, Drupal 7 requires Drupal 5.1 or newer. However, if one were to use, say, closures (a feature introduced in PHP 5.3), then the following line would need to be added: php = 5.3 Next, every .info file must declare which files in the module contain PHP functions, classes, or interfaces. This is done using the files[] directive. Our small initial module will only have one file, first.module. So we need only one files[] directive. files[] = first.module More complex files will often have several files[] directives, each declaring a separate PHP source code file. JavaScript, CSS, image files, and PHP files (like templates) that do not contain functions that the module needs to know about needn't be included in files[] directives. The point of the directive is simply to indicate to Drupal that these files should be examined by Drupal. One directive that we will not use for this module, but which plays a very important role is the dependencies[] directive. This is used to list the other modules that must be installed and active for this module to function correctly. Drupal will not allow a module to be enabled unless its dependencies have been satisfied. Drupal does not contain a directive to indicate that another module is recommended or is optional. It is the task of the developer to appropriately document this fact and make it known. There is currently no recommended best practice to provide such information. Now we have created our first.info file. As soon as Drupal reads this file, the module will appear on our Modules page. In the screenshot, notice that the module appears in the DRUPAL 7 DEVELOPMENT package, and has the NAME and DESCRIPTION as assigned in the .info file. With our .info file completed, we can now move on and code our .module file. Modules checked into Drupal's version control system will automatically have a version directive added to the .info file. This should typically not be altered. Creating a module file The .module file is a PHP file that conventionally contains all of the major hook implementations for a module. We will gain some practical knowledge of them. A hook implementation is a function that follows a certain naming pattern in order to indicate to Drupal that it should be used as a callback for a particular event in the Drupal system. For Object-oriented programmers, it may be helpful to think of a hook as similar to the Observer design pattern. When Drupal encounters an event for which there is a hook (and there are hundreds of such events), Drupal will look through all of the modules for matching hook implementations. It will then execute each hook implementation, one after another. Once all hook implementations have been executed, Drupal will continue its processing. In the past, all Drupal hook implementations had to reside in the .module file. Drupal 7's requirements are more lenient, but in most moderately sized modules, it is still preferable to store most hook implementations in the .module file. There are cases where hook implementations belong in other files. In such cases, the reasons for organizing the module in such a way will be explained. To begin, we will create a simple .module file that contains a single hook implementation – one that provides help information. <?php // $Id$ /** * @file * A module exemplifying Drupal coding practices and APIs. * * This module provides a block that lists all of the * installed modules. It illustrates coding standards, * practices, and API use for Drupal 7. */ /** * Implements hook_help(). */ function first_help($path, $arg) { if ($path == 'admin/help#first') { return t('A demonstration module.'); } } Before we get to the code itself, we will talk about a few stylistic items. To begin, notice that this file, like the .info file, contains an $Id$ marker that CVS will replace when the file is checked in. All PHP files should have this marker following a double-slash-style comment: // $Id$. Next, the preceding code illustrates a few of the important coding standards for Drupal. Source code standards Drupal has a thorough and strictly enforced set of coding standards. All core code adheres to these standards. Most add-on modules do, too. (Those that don't generally receive bug reports for not conforming.) Before you begin coding, it is a good idea to familiarize yourself with the standards as documented here: http://drupal.org/coding-standards. The Coder module can evaluate your code and alert you to any infringement upon the coding standards. We will adhere to the Drupal coding standards. In many cases, we will explain the standards as we go along. Still, the definitive source for standards is the URL listed above, not our code here. We will not re-iterate the coding standards. The details can be found online. However, several prominent standards deserve immediate mention. I will just mention them here, and we will see examples in action as we work through the code. Indenting: All PHP and JavaScript files use two spaces to indent. Tabs are never used for code formatting. The <?php ?> processor instruction: Files that are completely PHP should begin with <?php, but should omit the closing ?>. This is done for several reasons, most notably to prevent the inclusion of whitespace from breaking HTTP headers. Comments: Drupal uses Doxygen-style (/** */) doc-blocks to comment functions, classes, interfaces, constants, files, and globals. All other comments should use the double-slash (//) comment. The pound sign (#) should not be used for commenting. Spaces around operators: Most operators should have a whitespace character on each side. Spacing in control structures: Control structures should have spaces after the name and before the curly brace. The bodies of all control structures should be surrounded by curly braces, and even that of if statements with one-line bodies. Functions: Functions should be named in lowercase letters using underscores to separate words. Later we will see how class method names differ from this. Variables: Variable names should be in all lowercase letters using underscores to separate words. Member variables in objects are named differently. As we work through examples, we will see these and other standards in action. As we work through examples, we will see these and other standards in action.
Read more
  • 0
  • 0
  • 6880

article-image-report-finds-wordpress-leading-web-cms-market
Packt
13 Dec 2010
2 min read
Save for later

Report Finds WordPress Leading the Web CMS Market

Packt
13 Dec 2010
2 min read
WordPress had a phenomenal year. Our research showed downloads up over 100% and pageviews for the project site up nearly 200%. Publishing and developer support also increased dramatically since the 2009 Report,” notes lead analyst Ric Shreves. “While many of the other systems had good years, they were simply outpaced by the strides made by the WordPress CMS.” Though PHP-based systems remain the populist choice, the survey placed two .NET and three Java systems in the Top 20. DotNetNuke remained the market leader in the .NET space. Liferay leads the Java market for the first time this year, with Alfresco trailing closely behind. The findings in this year’s report were based on an open survey of more than 5,000 CMS users, together with research into a wide variety of measures of market share and brand strength. “In addition to having a much larger survey set this year, the Report benefits significantly from the historical data we’ve accumulated across the last three years, allowing us to place the trends into context and reach more meaningful conclusions,” adds Shreves. The Open Source CMS Market Share Report was launched in 2008 by digital agency water&stone. The Report is non-commercial and released under an open license. Since its inception, the Open Source CMS Market Share Report has become the industry standard for purpose of defining market share and adoption rates in the open source content management system market. >> You can download a copy of the 2009 Open Source CMS Market Share Report from http://www.waterandstone.com/book/2010-open-source-cms-market-share-report
Read more
  • 0
  • 0
  • 4653
Modal Close icon
Modal Close icon