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
All Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletter Hub
Free Learning
Arrow right icon
timer SALE ENDS IN
0 Days
:
00 Hours
:
00 Minutes
:
00 Seconds

How-To Tutorials - Front-End Web Development

341 Articles
article-image-load-validate-and-submit-forms-using-ext-js-30-part-3
Packt
19 Nov 2009
4 min read
Save for later

Load, Validate, and Submit Forms using Ext JS 3.0: Part 3

Packt
19 Nov 2009
4 min read
Loading form data from the server An important part of working with forms is loading the data that a form will display. Here's how to create a sample contact form and populate it with data sent from the server. How to do it... Declare the name and company panel: var nameAndCompany = { columnWidth: .5, layout: 'form', items: [ { xtype: 'textfield', fieldLabel: 'First Name', name: 'firstName', anchor: '95%' }, { xtype: 'textfield', fieldLabel: 'Last Name', name: 'lastName', anchor: '95%' }, { xtype: 'textfield', fieldLabel: 'Company', name: 'company', anchor: '95%' }, { xtype: 'textfield', fieldLabel: 'Title', name: 'title', anchor: '95%' } ]} Declare the picture box panel: var picBox = { columnWidth: .5, bodyStyle: 'padding:0px 0px 0px 40px', items: [ { xtype: 'box', autoEl: { tag: 'div', style: 'padding-bottom:20px', html: '<img id="pic" src="' + Ext.BLANK_IMAGE_URL + '" class="img-contact" />' } }, { xtype: 'button', text: 'Change Picture' } ]} Define the Internet panel: var internet = { columnWidth: .5, layout: 'form', items: [ { xtype: 'fieldset', title: 'Internet', autoHeight: true, defaultType: 'textfield', items: [{ fieldLabel: 'Email', name: 'email', vtype: 'email', anchor: '95%' }, { fieldLabel: 'Web page', name: 'webPage', vtype: 'url', anchor: '95%' }, { fieldLabel: 'IM', name: 'imAddress', anchor: '95%' }] }]} Declare the phone panel: var phones = { columnWidth: .5, layout: 'form', items: [{ xtype: 'fieldset', title: 'Phone Numbers', autoHeight: true, defaultType: 'textfield', items: [{ fieldLabel: 'Home', name: 'homePhone', anchor: '95%' }, { fieldLabel: 'Business', name: 'busPhone', anchor: '95%' }, { fieldLabel: 'Mobile', name: 'mobPhone', anchor: '95%' }, { fieldLabel: 'Fax', name: 'fax', anchor: '95%' }] }]} Define the business address panel: var busAddress = { columnWidth: .5, layout: 'form', labelAlign: 'top', defaultType: 'textarea', items: [{ fieldLabel: 'Business', labelSeparator:'', name: 'bAddress', anchor: '95%' }, { xtype: 'radio', boxLabel: 'Mailing Address', hideLabel: true, name: 'mailingAddress', value:'bAddress', id:'mailToBAddress' }]} Define the home address panel: var homeAddress = { columnWidth: .5, layout: 'form', labelAlign: 'top', defaultType: 'textarea', items: [{ fieldLabel: 'Home', labelSeparator:'', name: 'hAddress', anchor: '95%' }, { xtype: 'radio', boxLabel: 'Mailing Address', hideLabel: true, name: 'mailingAddress', value:'hAddress', id:'mailToHAddress' }]} Create the contact form: var contactForm = new Ext.FormPanel({ frame: true, title: 'TODO: Load title dynamically', bodyStyle: 'padding:5px', width: 650, items: [{ bodyStyle: { margin: '0px 0px 15px 0px' }, items: [{ layout: 'column', items: [nameAndCompany, picBox] }] }, { items: [{ layout: 'column', items: [phones, internet] }] }, { xtype: 'fieldset', title: 'Addresses', autoHeight: true, hideBorders: true, layout: 'column', items: [busAddress, homeAddress] }], buttons: [{ text: 'Save' }, { text: 'Cancel' }]}); Handle the form's actioncomplete event: contactForm.on({ actioncomplete: function(form, action){ if(action.type == 'load'){ var contact = action.result.data; Ext.getCmp(contact.mailingAddress).setValue(true); contactForm.setTitle(contact.firstName + ' ' + contact.lastName); Ext.getDom('pic').src = contact.pic; } }}); Render the form: contactForm.render(document.body); Finally, load the form: contactForm.getForm().load({ url: 'contact.php', params:{id:'contact1'}, waitMsg: 'Loading'}); How it works... The contact form's building sequence consists of defining each of the contained panels, and then defining a form panel that will serve as a host. The following screenshot shows the resulting form, with the placement of each of the panels pinpointed: Moving on to how the form is populated, the JSON-encoded response to a request to provide form data has a structure similar to this: {success:true,data:{id:'1',firstName:'Jorge',lastName:'Ramon',company:'MiamiCoder',title:'Mr',pic:'img/jorger.jpg',email:'[email protected]',webPage:'http://www.miamicoder.com',imAddress:'',homePhone:'',busPhone:'555 555-5555',mobPhone:'',fax:'',bAddress:'123 Acme Rd #001nMiami, FL 33133',hAddress:'',mailingAddress:'mailToBAddress'}} The success property indicates whether the request has succeeded or not. If the request succeeds, success is accompanied by a data property, which contains the contact's information. Although some fields are automatically populated after a call to load(), the form's title, the contact's picture, and the mailing address radio button require further processing. This can be done in the handler for the actioncomplete event: contactForm.on({ actioncomplete: function(form, action){ if(action.type == 'load'){} }}); As already mentioned, the contact's information arrives in the data property of the action's result: var contact = action.result.data; The default mailing address comes in the contact's mailingAddress property. Hence, the radio button for the default mailing address is set as shown in the following line of code: Ext.getCmp(contact.mailingAddress).setValue(true); The source for the contact's photo is the value of contact.pic: Ext.getDom('pic').src = contact.pic; And finally, the title of the form: contactForm.setTitle(contact.firstName + ' ' + contact.lastName); There's more... Although this recipe's focus is on loading form data, you should also pay attention to the layout techniques used—multiple rows, multiple columns, fieldsets—that allow you to achieve rich and flexible user interfaces for your forms. See Also... The next recipe, Serving the XML data to a form, explains how to use a form to load the XML data sent from the server.
Read more
  • 0
  • 0
  • 3593

article-image-making-progress-menus-and-toolbars-using-ext-js-30-part-2
Packt
19 Nov 2009
6 min read
Save for later

Making Progress with Menus and Toolbars using Ext JS 3.0: Part 2

Packt
19 Nov 2009
6 min read
Embedding a progress bar in a status bar This topic explains how to embed a progress bar in a panel's status bar, a scenario found in countless user interfaces: How to do it Create a click handler that will simulate a long-running activity and update the progress bar: Ext.onReady(function() { var loadFn = function(btn, statusBar) { btn = Ext.getCmp(btn); btn.disable(); Ext.fly('statusTxt').update('Saving...'); pBar.wait({ interval: 200, duration: 5000, increment: 15, fn: function() { btn.enable(); Ext.fly('statusTxt').update('Done'); } });}; Create an instance of the progress bar: var pBar = new Ext.ProgressBar({ id: 'pBar', width: 100}); Create a host panel and embed the progress bar in the bbar of the panel. Also, add a button that will start the progress bar updates: var pnl = new Ext.Panel({ title: 'Status bar with progress bar', renderTo: 'pnl1', width: 400, height: 200, bodyStyle: 'padding:10px;', items: [{ xtype: 'button', id: 'btn', text: 'Save', width:'75', handler: loadFn.createCallback('btn', 'sBar') }], bbar: { id: 'sBar', items: [{ xtype: 'tbtext', text: '',id:'statusTxt' },'->', pBar] }}); How it works The first step consists of creating loadFn, a function that simulates a long-running operation, so that we can see the progress bar animation when the button is clicked. The heart of loadFn is a call to ProgressBar.wait(…), which initiates the progress bar in an auto-update mode. And this is how the status bar is embedded in the bbar of the panel: bbar: { id: 'sBar', items: [{ xtype: 'tbtext', text: '',id:'statusTxt' },'->', pBar] Observe how the progress bar is sent to the rightmost location in the status bar with the help of a Toolbar.Fill instance, declared with '->'. Creating a custom look for the status bar items Customizing the look of toolbar items is relatively simple. In this recipe, you will learn how to create toolbar items with a sunken look that can be found in many desktop applications: How to do it Create the styles that will provide the custom look of the status bar text items: .custom-status-text-panel{ border-top:1px solid #99BBE8; border-right:1px solid #fff; border-bottom:1px solid #fff; border-left:1px solid #99BBE8; padding:1px 2px 2px 1px;} Create a host panel: Ext.onReady(function() { var pnl = new Ext.Panel({ title: 'Status bar with sunken text items', renderTo: 'pnl1', width: 400, height: 200, bodyStyle: 'padding:10px;', Define the panel's bbar with the text items: bbar: { id: 'sBar', items: [ { id: 'cachedCount', xtype:'tbtext', text: 'Cached: 15' }, ' ', { id: 'uploadedCount', xtype: 'tbtext', text: 'Uploaded: 7' }, ' ', { id: 'invalidCount', xtype: 'tbtext', text: 'Invalid: 2' } ]}, Now, add a handler for the afterrender event and use it to modify the styles of the text items: listeners: { 'afterrender': { fn: function() { Ext.fly(Ext.getCmp('cachedCount').getEl()).parent(). addClass('custom-status-text-panel'); Ext.fly(Ext.getCmp('uploadedCount').getEl()).parent(). addClass('custom-status-text-panel'); Ext.fly(Ext.getCmp('invalidCount').getEl()).parent(). addClass('custom-status-text-panel'); }, delay:500 }} How it works The actual look of the items is defined by the style in the custom-status-text-panel CSS class. After the host panel and toolbar are created and rendered, the look of the items is changed by applying the style to each of the TD elements that contain the items. For example: Ext.fly(Ext.getCmp('uploadedCount').getEl()).parent(). addClass('custom-status-text-panel'); See also... The previous recipe, Embedding a progress bar in a status bar, explains how a progress bar can be embedded in a panel's status bar Using a progress bar to indicate that your application is busy In this topic, you will learn how to use a progress bar to indicate that your application is busy performing an operation. The next screenshot shows a progress bar built using this recipe: How to do it Define the progress bar: Ext.onReady(function() { Ext.QuickTips.init(); var pBar = new Ext.ProgressBar({ id: 'pBar', width: 300, renderTo: 'pBarDiv'}); Add a handler for the update event and use it to update the wait message: pBar.on('update', function(val) { //Handle this event if you need to // execute code at each progress interval. Ext.fly('pBarText').dom.innerHTML += '.';}); Create a click handler for the button that will simulate a long-running activity: var btn = Ext.get('btn');btn.on('click', function() { Ext.fly('pBarText').update('Please wait'); btn.dom.disabled = true; pBar.wait({ interval: 200, duration: 5000, increment: 15, fn: function() { btn.dom.disabled = false; Ext.fly('pBarText').update('Done'); } });}); Add the button to the page: <button id="btn">Start long-running operation</button> How it works After creating the progress bar, the handler for its update event is created. While I use this handler simply to update the text message, you can use it to execute some other code every time that a progress interval occurs. The click handler for the button calls the progress bar's wait(…) function, which causes the progress bar to auto-update at the configured interval and reset itself after the configured duration: pBar.wait({ interval: 200, duration: 5000, increment: 15, fn: function() { btn.dom.disabled = false; Ext.fly('pBarText').update('Done'); }}); There's more The progress bar can also be configured to run indefinitely by not passing the duration config option. Clearing the progress bar in this scenario requires a call to the reset() function. See also... The next recipe, Using a progress bar to report progress updates, illustrates how a progress bar can be set up to notify the user that progress is being made in the execution of an operation The Changing the look of a progress bar recipe (covered later in this article) shows you how easy it is to change the look of the progress bar using custom styles Summary This article consisted recipes that examined the commonly-used menu items, as well as the different ways of setting up toolbars and progress bars in your applications. [ 1 | 2 ] If you have read this article you may be interested to view :   Making Progress with Menus and Toolbars using Ext JS 3.0: Part 1 Load, Validate, and Submit Forms using Ext JS 3.0: Part 1 Load, Validate, and Submit Forms using Ext JS 3.0: Part 2 Load, Validate, and Submit Forms using Ext JS 3.0: Part 3
Read more
  • 0
  • 0
  • 1816

article-image-load-validate-and-submit-forms-using-ext-js-30-part-2
Packt
19 Nov 2009
4 min read
Save for later

Load, Validate, and Submit Forms using Ext JS 3.0: Part 2

Packt
19 Nov 2009
4 min read
Creating validation functions for URLs, email addresses, and other types of data Ext JS has an extensive library of validation functions. This is how it can be used to validate URLs, email addresses, and other types of data. The following screenshot shows email address validation in action: This screenshot displays URL validation in action: How to do it... Initialize the QuickTips singleton: Ext.QuickTips.init(); Create a form with fields that accept specific data formats: Ext.onReady(function() { var commentForm = new Ext.FormPanel({ frame: true, title: 'Send your comments', bodyStyle: 'padding:5px', width: 550, layout: 'form', defaults: { msgTarget: 'side' }, items: [ { xtype: 'textfield', fieldLabel: 'Name', name: 'name', anchor: '95%', allowBlank: false }, { xtype: 'textfield', fieldLabel: 'Email', name: 'email', anchor: '95%', vtype: 'email' }, { xtype: 'textfield', fieldLabel: 'Web page', name: 'webPage', vtype: 'url', anchor: '95%' }, { xtype: 'textarea', fieldLabel: 'Comments', name: 'comments', anchor: '95%', height: 150, allowBlank: false }], buttons: [{ text: 'Send' }, { text: 'Cancel' }] }); commentForm.render(document.body);}); How it works... The vtype configuration option specifies which validation function will be applied to the field. There's more... Validation types in Ext JS include alphanumeric, numeric, URL, and email formats. You can extend this feature with custom validation functions, and virtually, any format can be validated. For example, the following code shows how you can add a validation type for JPG and PNG files: Ext.apply(Ext.form.VTypes, { Picture: function(v) { return /^.*.(jpg|JPG|png|PNG)$/.test(v); }, PictureText: 'Must be a JPG or PNG file';}); If you need to replace the default error text provided by the validation type, you can do so by using the vtypeText configuration option: { xtype: 'textfield', fieldLabel: 'Web page', name: 'webPage', vtype: 'url', vtypeText: 'I am afraid that you did not enter a URL', anchor: '95%'} See also... The Specifying the required fields in a form recipe, covered earlier in this article, explains how to make some form fields required The Setting the minimum and maximum length allowed for a field's value recipe, covered earlier in this article, explains how to restrict the number of characters entered in a field The Changing the location where validation errors are displayed recipe, covered earlier in this article, shows how to relocate a field's error icon Refer to the previous recipe, Deferring field validation until form submission, to know how to validate all fields at once upon form submission, instead of using the default automatic field validation The next recipe, Confirming passwords and validating dates using relational field validation, explains how to perform validation when the value of one field depends on the value of another field The Rounding up your validation strategy with server-side validation of form fields recipe (covered later in this article) explains how to perform server-side validation Confirming passwords and validating dates using relational field validation Frequently, you face scenarios where the values of two fields need to match, or the value of one field depends on the value of another field. Let's examine how to build a registration form that requires the user to confirm his or her password when signing up. How to do it… Initialize the QuickTips singleton: Ext.QuickTips.init(); Create a custom vtype to handle the relational validation of the password: Ext.apply(Ext.form.VTypes, { password: function(val, field) { if (field.initialPassField) { var pwd = Ext.getCmp(field.initialPassField); return (val == pwd.getValue()); } return true; }, passwordText: 'What are you doing?<br/>The passwords entered do not match!'}); Create the signup form: var signupForm = { xtype: 'form', id: 'register-form', labelWidth: 125, bodyStyle: 'padding:15px;background:transparent', border: false, url: 'signup.php', items: [ { xtype: 'box', autoEl: { tag: 'div', html: '<div class="app-msg"><img src="img/businessman add.png" class="app-img" /> Register for The Magic Forum</div>' } }, { xtype: 'textfield', id: 'email', fieldLabel: 'Email', allowBlank: false, minLength: 3, maxLength: 64,anchor:'90%', vtype:'email' }, { xtype: 'textfield', id: 'pwd', fieldLabel: 'Password', inputType: 'password',allowBlank: false, minLength: 6, maxLength: 32,anchor:'90%', minLengthText: 'Password must be at least 6 characters long.' }, { xtype: 'textfield', id: 'pwd-confirm', fieldLabel: 'Confirm Password', inputType: 'password', allowBlank: false, minLength: 6, maxLength: 32,anchor:'90%', minLengthText: 'Password must be at least 6 characters long.', vtype: 'password', initialPassField: 'pwd' }],buttons: [{ text: 'Register', handler: function() { Ext.getCmp('register-form').getForm().submit(); }},{ text: 'Cancel', handler: function() { win.hide(); } }]} Create the window that will host the signup form: Ext.onReady(function() { win = new Ext.Window({ layout: 'form', width: 340, autoHeight: true, closeAction: 'hide', items: [signupForm] }); win.show();
Read more
  • 0
  • 0
  • 3904
Banner background image

article-image-user-interaction-and-email-automation-symfony-13-part2
Packt
18 Nov 2009
8 min read
Save for later

User Interaction and Email Automation in Symfony 1.3: Part2

Packt
18 Nov 2009
8 min read
Automated email responses Symfony comes with a default mailer library that is based on Swift Mailer 4, the detailed documentation is available from their web site at http://swiftmailer.org. After a user has signed up to our mailing list, we would like an email verification to be sent to the user's email address. This will inform the user that he/she has signed up, and will also ask him or her to activate their subscription. To use the library, we have to complete the following three steps: Store the mailing settings in the application settings file. Add the application logic to the action. Create the email template. Adding the mailer settings to the application Just like all the previous settings, we should add all the settings for sending emails to the module.yml file for the signup module. This will make it easier to implement any modifications required later. Initially, we should set variables like the email subject, the from name, the from address, and whether we want to send out emails within the dev environment. I have added the following items to our signup module's setting file, apps/frontend/config/module.yml: dev: mailer_deliver: true all: mailer_deliver: true mailer_subject: Milkshake Newsletter mailer_from_name: Tim mailer_from_email: no-reply@milkshake All of the settings can be contained under the all label. However, you can see that I have introduced a new label called dev. These labels represent the environments, and we have just added a specific variable to the dev environment. This setting will allow us to eventually turn off the sending of emails while in the dev environment. Creating the application logic Triggering the email should occur after the user's details have been saved to the database. To demonstrate this, I have added the highlighted amends to the submit action in the apps/frontend/modules/signup/actions/actions.class.php file, as shown in the following code: public function executeSubmit(sfWebRequest $request) { $this->form = new NewsletterSignupForm(); if ($request->isMethod('post') && $this->form-> bindAndSave($request->getParameter($this->form-> getName()))) { //Include the swift lib require_once('lib/vendor/swift-mailer/lib/swift_init.php'); try{ //Sendmail $transport = Swift_SendmailTransport::newInstance(); $mailBody = $this->getPartial('activationEmail', array('name' => $this->form->getValue('first_name'))); $mailer = Swift_Mailer::newInstance($transport); $message = Swift_Message::newInstance(); $message->setSubject(sfConfig::get('app_mailer_subject')); $message->setFrom(array(sfConfig:: get('app_mailer_from_email') => sfConfig::get('app_mailer_from_name'))); $message->setTo(array($this->form->getValue('email')=> $this-> form->getValue('first_name'))); $message->setBody($mailBody, 'text/html'); if(sfConfig::get('app_mailer_deliver')) { $result = $mailer->send($message); } } catch(Exception $e) { var_dump($e); exit; } $this->redirect('@signup'); } //Use the index template as it contains the form $this->setTemplate('index'); } Symfony comes with a sfMailer class that extends Swift_Mailer. To send mails you could simply implement the following Symfony method: $this->getMailer()->composeAndSend('[email protected]', '[email protected]', 'Subject', 'Body'); Let's walk through the process: Instantiate the Swift Mailer. Retrieve the email template (which we will create next) using the $this->getPartial('activationEmail', array('name' => $this->form->getValue('first_name'))) method. Breaking this down, the function itself retrieves a partial template. The first argument is the name of the template to retrieve (that is activationEmail in our example) which, if you remember, means that the template will be called _activationEmail.php. The next argument is an array that contains variables related to the partial template. Here, I have set a name variable. The value for the name is important. Notice how I have used the value within the form object to retrieve the first_name value. This is because we know that these values have been cleaned and are safe to use. Set the subject, from, to, and the body items. These functions are Swift Mailer specific: setSubject(): It takes a string as an argument for the subject setFrom(): It takes the name and the mailing address setTo(): It takes the name and the mailing address setBody(): It takes the email body and mime type. Here we passed in our template and set the email to text/html Finally we send the email. There are more methods in Swift Mailer. Check out the documentation on the Swift Mailer web site (http://swiftmailer.org/). The partial email template Lastly, we need to create a partial template that will be used in the email body. In the templates folder of the signup module, create a file called _activationEmail.php and add the following code to it: Hi <?php echo $name; ?>, <br /><br /> Thank you for signing up to our newsletter. <br /><br /> Thank you, <br /> <strong>The Team</strong> The partial is no different from a regular template. We could have opted to pass on the body as a string, but using the template keeps our code uniform. Our signup process now incorporates the functionality to send an email. The purpose of this example is to show you how to send an automated email using a third-party library. For a real application, you should most certainly implement a two-phase option wherein the user must verify his or her action. Flashing temporary values Sometimes it is necessary to set a temporary variable for one request, or make a variable available to another action after forwarding but before having to delete the variable. Symfony provides this level of functionality within the sfUser object known as a flash variable. Once a flash variable has been set, it lasts until the end of the overall request before it is automatically destroyed. Setting and getting a flash attribute is managed through two of the sfUser methods. Also, you can test for a flash variable's existence using the third method of the methods listed here: $this->getUser()->setFlash($name, $value, $persist = true) $this->getUser()->getFlash($name) $this->getUser()->hasFlash($name) Although a flash variable will be available by default when a request is forwarded to another action, setting the argument to false will delete the flash variable before it is forwarded. To demonstrate how useful flash variables can be, let's readdress the signup form. After a user submits the signup form, the form is redisplayed. I further mentioned that you could create another action to handle a 'thank you' template. However, by using a flash variable we will not have to do so. As a part of the application logic for the form submission, we can set a flash variable. Then after the action redirects the request, the template can test whether there is a flash variable set. If there is one, the template should show a message rather than the form. Let's add the $this->getUser()->setFlash() function to the submit action in the apps/frontend/modules/signup/actions/actions.class.php file: //Include the swift lib require_once('lib/vendor/swift-mailer/lib/swift_init.php'); //set Flash $this->getUser()->setFlash('Form', 'completed'); try{ I have added the flash variable just under the require_once() statement. After the user has submitted a valid form, this flash variable will be set with the name of the Form and have a value completed. Next, we need to address the template logic. The template needs to check whether a flash variable called Form is set. If it is not set, the template shows the form. Otherwise it shows a thank you message. This is implemented using the following code: <?php if(!$sf_user->hasFlash('Form')): ?> <form action="<?php echo url_for('@signup_submit') ?>" method="post" name="Newsletter"> <div style="height: 30px;"> <div style="width: 150px; float: left"> <?php echo $form['first_name']->renderLabel() ?></div> <?php echo $form['first_name']->render(($form['first_name']-> hasError())? array('class'=>'boxError'): array ('class'=>'box')) ?> <?php echo ($form['first_name']->hasError())? ' <span class="errorMessage">* '.$form['first_name']->getError(). '</span>': '' ?> <div style="clear: both"></div> </div> .... </form> <?php else: ?><h1>Thank you</h1>You are now signed up.<?php endif ?> The form is now wrapped inside an if/else block. Accessing the flash variables from a template is done through $sf_user. To test if the variable has been set, I have used the hasFlash() method, $sf_user->hasFlash('Form'). The else part of the statement contains the text rather than the form. Now if you submit your form, you will see the result as shown in the following screenshot: We have now implemented an entire module for a user to sign up for our newsletter. Wouldn't it be really good if we could add this module to another application without all the copying, pasting, and fixing?
Read more
  • 0
  • 0
  • 1244

article-image-user-interaction-and-email-automation-symfony-13-part1
Packt
18 Nov 2009
14 min read
Save for later

User Interaction and Email Automation in Symfony 1.3: Part1

Packt
18 Nov 2009
14 min read
The signup module We want to provide the users with the functionality to enter their name, email address, and how they found our web site. We want all this stored in a database and to have an email automatically sent out to the users thanking them for signing up. To start things off, we must first add some new tables to our existing database schema. The structure of our newsletter table will be straightforward. We will need one table to capture the users' information and a related table that will hold the names of all the places where we advertised our site. I have constructed the following entity relationship diagram to show you a visual relationship of the tables: All the code used in this article can be accessed here. Let's translate this diagram into XML and place it in the config/schema.xml file: <table name="newsletter_adverts" idMethod="native" phpName="NewsletterAds"> <column name="newsletter_adverts_id" type="INTEGER" required="true" autoIncrement="true" primaryKey="true" /> <column name="advertised" type="VARCHAR" size="30" required="true" /> </table> <table name="newsletter_signups" idMethod="native" phpName="NewsletterSignup"> <column name="id" type="INTEGER" required="true" autoIncrement="true" primaryKey="true" /> <column name="first_name" type="VARCHAR" size="20" required="true" /> <column name="surname" type="VARCHAR" size="20" required="true" /> <column name="email" type="VARCHAR" size="100" required="true" /> <column name="activation_key" type="VARCHAR" size="100" required="true" /> <column name="activated" type="BOOLEAN" default="0" required="true" /> <column name="newsletter_adverts_id" type="INTEGER" required="true"/> <foreign-key foreignTable="newsletter_adverts" onDelete="CASCADE"> <reference local="newsletter_adverts_id" foreign="newsletter_adverts_id" /> </foreign-key> <column name="created_at" type="TIMESTAMP" required="true" /> <column name="updated_at" type="TIMESTAMP" required="true" /> </table> We will need to populate the newsletter_adverts table with some test data as well. Therefore, I have also appended the following data to the fixtures.yml file located in the data/fixtures/ directory: NewsletterAds: nsa1: advertised: Internet Search nsa2: advertised: High Street nsa3: advertised: Poster With the database schema and the test data ready to be inserted into the database, we can once again use the Symfony tasks. As we have added two new tables to the schema, we will have to rebuild everything to generate the models using the following command: $/home/timmy/workspace/milkshake>symfony propel:build-all-load --no-confirmation Now we have populated the tables in the database, and the models and forms have been generated for use too. Binding a form to a database table Symfony contains a whole framework just for the development of forms. The forms framework makes building forms easier by applying object-oriented methods to their development. Each form class is based on its related table in the database. This includes the fields, the validators, and the way in which the forms and fields are rendered. A look at the generated base class Rather than starting off with a simple form, we are going to look at the base form class that has already been generated for us as a part of the build task we executed earlier. Because the code is generated, it will be easier for you to see the initial flow of a form. So let's open the base class for the NewsletterSignupForm form. The file is located at lib/form/base/BaseNewsletterSignupForm.class.php: class BaseNewsletterSignupForm extends BaseFormPropel { public function setup() { $this->setWidgets(array( 'id' => new sfWidgetFormInputHidden(), 'first_name' => new sfWidgetFormInput(), 'surname' => new sfWidgetFormInput(), 'email' => new sfWidgetFormInput(), 'activation_key' => new sfWidgetFormInput(), 'activated' => new sfWidgetFormInputCheckbox(), 'newsletter_adverts_id' => new sfWidgetFormPropelChoice (array('model' => 'NewsletterAds', 'add_empty' => false)), 'created_at' => new sfWidgetFormDateTime(), 'updated_at' => new sfWidgetFormDateTime(), )); $this->setValidators(array( 'id' => new sfValidatorPropelChoice(array ('model' => 'NewsletterSignup', 'column' => 'id', 'required' => false)), 'first_name' => new sfValidatorString(array('max_length' => 20)), 'surname' => new sfValidatorString(array('max_length' => 20)), 'email' => new sfValidatorString(array('max_length' => 100)), 'activation_key' => new sfValidatorString(array('max_length' => 100)), 'activated' => new sfValidatorBoolean(), 'newsletter_adverts_id'=> new sfValidatorPropelChoice(array ('model' => 'NewsletterAds', 'column' => 'newsletter_adverts_id')), 'created_at' => new sfValidatorDateTime(), 'updated_at' => new sfValidatorDateTime(), )); $this->widgetSchema->setNameFormat('newsletter_signup[%s]'); $this->errorSchema = new sfValidatorErrorSchema ($this->validatorSchema); parent::setup(); } There are five areas in this base class that are worth noting: This base class extends the BaseFormPropel class, which is an empty class. All base classes extend this class, which allows us to add global settings to all our forms. All of the columns in our table are treated as fields in the form, and are referred to as widgets. All of these widgets are then attached to the form by adding them to the setWidgets() method. Looking over the widgets in the array, you will see that they are pretty standard, such as sfWidgetFormInputHidden(), sfWidgetFormInput(). However, there is one widget added that follows the relationship between the newsletter_sigups table and the newsletter_adverts table. It is the sfWidgetFormPropelChoice widget. Because there is a 1:M relation between the tables, the default behavior is to use this widget, which creates an HTML drop-down box and is populated with the values from the newsletter_adverts table. As a part of the attribute set, you will see that it has set the model needed to retrieve the values to NewsletterAds and the newsletter_adverts_id column for the actual values of the drop-down box. All the widgets on the form must be validated by default. To do this, we have to call the setValidators() method and add the validation requirements to each widget. At the moment, the generated validators reflect the attributes of our database as set in the schema. For example, the first_name field in the statement 'first_name' => new sfValidatorString(array('max_length' => 20)) demonstrates that the validator checks if the maximum length is 20. If you remember, in our schema too, the first_name column is set to 20 characters. The final part calls the parent's setup() function. The base class BaseNewsletterSignupForm contains all the components needed to generate the form for us. So let's get the form on a page and take a look at the method to customize it. There are many widgets that Symfony provides for us. You can find the classes for them inside the widget/ directory of your Symfony installation. The Symfony propel task always generates a form class and its corresponding base class. Of course, not all of our tables will need to have a form bound to them. Therefore, delete all the form classes that are not needed. Rendering the form Rendering this basic form requires us to instantiate the form object in the action. Assigning the form object to the global $this variable means that we can pass the form object to the template just like any other variable. So let's start by implementing the newsletter signup module. In your terminal window, execute the generate:module task like this: $/home/timmy/workspace/milkshake>symfony generate:module frontend signup Now we can start with the application logic. Open the action class from apps/frontend/modules/signup/actions/actions.class.php for the signup module and add the following logic inside the index action: public function executeIndex(sfWebRequest $request) { $this->form = new NewsletterSignupForm(); return sfView::SUCCESS; } As I had mentioned earlier, the form class deals with the form validation and rendering. For the time being, we are going to stick to the default layout by allowing the form object to render itself. Using this method initially will allow us to create rapid prototypes. Let's open the apps/frontend/signup/templates/indexSuccess.php template and add the following view logic: <form action="<?php echo url_for('signup/submit') ?>" method="POST"> <table><?php echo $form ?></table> <input type="submit" /> </form> The form class is responsible for rendering of the form elements only. Therefore, we have to include the <form> and submit HTML tags that wrap around the form. Also, the default format of the form is set to 'table'. Again, we must also add the start and end tags of the <table>. At this stage, we would normally be able to view the form in the browser. But doing so will raise a Symfony exception error. The cause of this is that the results retrieved from the newsletter_adverts table are in the form of an array of objects. These results need to populate the select box widget. But in the current format, this is not possible. Therefore, we have to convert each object into its string equivalent. To do this, we need to create a PHP magic function of __toString() in the DAO class NewsletterAds. The DAO class for NewlsetterAds is located at lib/model/NewsletterAds.php just as all of the other models. Here we need to represent each object as its name, which is the value in the advertised column. Remember that we need to add this method to the DAO class as this represents a row within the results, unlike the peer class that represents the entire result set. Let's add the function to the NewsletterAds class as I have done here: class NewsletterAds extends BaseNewsletterAds { public function __toString() { return $this->getAdvertised(); } } We are now ready to view the completed form. In your web browser, enter the URL http://milkshake/frontend_dev.php/signup and you will see the result shown in the following screenshot: As you can see, although the form has been rendered according to our table structure, the fields which we do not want the user to fill in are also included. Of course, we can change this quiet easily. But before we take a look at the layout of the form, let's customize the widgets and widget validators. Now we can begin working on the application logic for submitting the form. Customizing form widgets and validators All of the generated form classes are located in the lib/form and the lib/form/base directories. The latter is where the default generated classes are located, and the former is where the customizable classes are located. This follows the same structure as the models. Each custom form class inherits from its parent. Therefore, we have to override some of the functions to customize the form. Let's customize the widgets and validators for the NewsletterSignupForm. Open the lib/forms/NewsletterSignupForm.class.php file and paste the following code inside the configure() method: //Removed unneeded widgets unset( $this['created_at'], $this['updated_at'], $this['activation_key'], $this['activated'], $this['id'] ); //Set widgets //Modify widgets $this->widgetSchema['first_name'] = new sfWidgetFormInput(); $this->widgetSchema['newsletter_adverts_id'] = new sfWidgetFormPropelChoice(array('model' => 'NewsletterAds', 'add_empty' => true, 'label'=>'Where did you find us?')); $this->widgetSchema['email'] = new sfWidgetFormInput (array('label' => 'Email Address')); //Add validation $this->setValidators(array ('first_name'=> new sfValidatorString(array ('required' => true), array('required' => 'Enter your firstname')), 'surname'=> new sfValidatorString(array('required' => true), array('required' => 'Enter your surname')), 'email'=> new sfValidatorString(array('required' => true), array('invalid' => 'Provide a valid email', 'required' => 'Enter your email')), 'newsletter_adverts_id' => new sfValidatorPropelChoice(array('model' => 'NewsletterAds', 'column' => 'newsletter_adverts_id'), array('required' => 'Select where you found us')), )); //Set post validators $this->validatorSchema->setPostValidator( new sfValidatorPropelUnique(array('model' => 'NewsletterSignup', 'column' => array('email')), array('invalid' => 'Email address is already registered')) ); //Set form name $this->widgetSchema->setNameFormat('newsletter_signup[%s]'); //Set the form format $this->widgetSchema->setFormFormatterName('list'); Let's take a closer look at the code. Removing unneeded fields To remove the fields that we do not want to be rendered, we must call the PHP unset() method and pass in the fields to unset. As mentioned earlier, all of the fields that are rendered need a corresponding validator, unless we unset them. Here we do not want the created_at and activation_key fields to be entered by the user. To do so, the unset() method should contain the following code: unset( $this['created_at'], $this['updated_at'], $this['activation_key'], $this['activated'], $this['id'] ); Modifying the form widgets Although it'll be fine to use the remaining widgets as they are, let's have a look at how we can modify them: //Modify widgets $this->widgetSchema['first_name'] = new sfWidgetFormInput(); $this->widgetSchema['newsletter_adverts_id'] = new sfWidgetFormPropelChoice(array('model' => 'AlSignupNewsletterAds', 'add_empty' => true, 'label'=>'Where did you find us?')); $this->widgetSchema['email'] = new sfWidgetFormInput(array('label' => 'Email Address')); There are several types of widgets available, but our form requires only two of them. Here we have used the sfWidgetFormInput() and sfWidgetFormPropelChoice() widgets. Each of these can be initialized with several values. We have initialized the email and newsletter_adverts_id widgets with a label. This basically renders the label field associated to the widget on the form. We do not have to include a label because Symfony adds the label according to the column name. Adding form validators Let's add the validators in a similar way as we have added the widgets: //Add validation $this->setValidators(array( 'first_name'=> new sfValidatorString(array('required' => true), array('required' => 'Enter your firstname')), 'surname'=> new sfValidatorString(array('required' => true), array('required' => 'Enter your surname')), 'email'=> new sfValidatorEmail(array('required' => true), array('invalid' => 'Provide a valid email', 'required' => 'Enter your email')), 'newsletter_adverts_id' => new sfValidatorPropelChoice(array ('model' => 'NewsletterAds', 'column' => 'newsletter_adverts_id'), array('required' => 'Select where you found us')), )); //Set post validators $this->validatorSchema->setPostValidator(new sfValidatorPropelUnique(array('model' => 'NewsletterSignup', 'column' => array('email')), array('invalid' => 'Email address is already registered')) ); Our form will need four different types of validators: sfValidatorString: This checks the validity of a string against a criteria. It takes four arguments—required, trim, min_length, and max_length. SfValidatorEmail: This validates the input against the pattern of an email address. SfValidatorPropelChoice: It validates the value with the values in the newsletter_adverts table. It needs the model and column that are to be used.   SfValidatorPropelUnique: Again, this validator checks the value against the values in a given table column for uniqueness. In our case, we want to use the NewsletterSignup model to test if the email column is unique. As mentioned earlier, all the fields must have a validator. Although it's not recommended, you can allow extra parameters to be passed in. To achieve this, there are two steps: You must disable the default option of having all fields validated by $this->validatorSchema->setOption('allow_extra_fields', true). Although the above step allows the values to bypass validation, they will be filtered out of the results. To prevent this, you will have to set $this->validatorSchema->setOption('filter_extra_fields', false). Form naming convention and setting its style The final part we added is the naming convention for the HTML attributes and the style in which we want the form rendered. The HTML output will use our naming convention. For example, in the following code, we have set the convention to newsletter_signup[fieldname] for each input field's name. //Set form name $this->widgetSchema->setNameFormat('newsletter_signup[%s]'); //Set the form format $this->widgetSchema->setFormFormatterName('list'); Two formats are shipped with Symfony that we can use to render our form. We can either render it in an HTML table or an unordered list. As we have seen, the default is an HTML table. But by setting this as list, the form is now rendered as an unordered HTML list, just like the following screenshot. (Of course, I had to replace the <table> tags with the <ul> tags.)
Read more
  • 0
  • 0
  • 1464

article-image-keyword-research-search-engine-optimization-drupal-6
Packt
10 Nov 2009
11 min read
Save for later

Keyword Research for Search Engine Optimization in Drupal 6

Packt
10 Nov 2009
11 min read
SEO is necessary—you've got to do it if you want to rank well for keywords. Simple in concept, keywords are actually very complicated things. They bring order to chaos, define markets, and reveal intent. Keyword data simultaneously tells you how many people are looking for your product or service and what those people will do once they find you. The results of a keyword search can tell you who the top people are in an industry and inform you of upcoming trends in the market. Keywords are the most visible focal point of free market competition between business interests. Search engine optimization is a popularity contest for keywords and this is a popularity contest you want to win. The most critical part of an SEO project is finding the right keywords. You will spend months working on your web site, getting links, and telling the world that your site is the authority on that keyword. It's critical that when you finally arrive, your customers are there to embrace you. If you pick the wrong keywords, you'll spend months working only to find that there is nobody who wants to buy your product. Ouch! An extra few hours researching your keywords in the beginning will help you avoid this fate. What a keyword is Keywords are many things to many people. For the purpose of this SEO campaign, there are really only two things about keywords that we need to understand to get the job done. Keywords aggregate searchers into organized groups and a keyword defines a market. Keywords are single words that a search engine user types into the search box to try to find what they're looking for. Key phrases are the same as keywords except for the fact that they consist of two or more words. For the sake of simplicity, throughout this article let's use keywords to mean both, keywords and key phrases. Keywords aggregate searchers into organized groups Millions of random people visit Google every day. When they arrive, they are amorphous—a huddled mass yearning for enlightenment with nothing more than a blank Google search form to guide them. As each person types keywords into Google and clicks the Search button, this random mass of people becomes extraordinarily organized. Each keyword identifies exactly what that person is looking for and allows Google to show them results that would satisfy their query. Much like a labor union, the more searchers there are looking for a particular phrase, the more clout they have with the businesses who want to sell to them. However, instead of more pay and better health benefits, you get better search results. If there are a thousand people per month looking for keyword A and a hundred people per month looking for keyword B, then chances are good that there are more competitors focused on keyword A. More competition means better optimization is required to show up at the top. Better optimization requires more content, closer attention to meeting the needs of the group, and more interesting web sites. A keyword defines a market This organization of searchers is what gives Google such power. In a very real way, Google creates billions of tiny markets every day. There is a buyer (the searcher) looking for a product, the seller (the web site owners) selling what they've got, and the middleman (Google) organizing everything and facilitating the transaction. The transaction takes place when the searcher clicks on the result of a keyword search and is whisked off to the seller's web site. However, it doesn't always go smoothly. In fact, very high percentages of the time the searcher doesn't find what they're looking for so they hit the back button and try again. They may try a different result on the same page or type in a different keyword and do the entire search again. Each time you have an opportunity to convince them that yours is the right site with the best information and most promising solution to their questions. It is in your best interest to provide a web site that quickly engages the searchers, pulls them in, and keeps the dialogue going. Why keyword research is important As a Drupal site owner, you have the opportunity to position yourself as the best site available for the keywords people are searching for. Know thy customerThere are hundreds of good marketing books out there to help you better understand your audience. All that good information applies to SEO as well. The better you know your audience, the better you can guess what keywords they are typing in Google to find companies like yours. You're an expert in your field, so of course you know many of the keywords that people use to find your products and services. But, are you sure you know them all? A few years ago Tom, a friend of mine, hired me to do SEO for his high-end landscaping firm. His company designs and installs yards, trees, retaining walls, and so on, outside million dollar homes in the hill country near Austin, Texas. We sat down in an early morning meeting and he said, "Ben, the right keyword is landscaping. I know it so there's no reason to do all this research. Don't waste your time and my money. Just do landscaping and that's that". Being the thorough person that I am, I did the keyword research anyway. Guess what I found? The number one phrase in his business was landscaping. However, a very close second was landscaper. And, while landscaping had dozens of competitors—some of them were very well entrenched—there were only a handful of competitors showing up for landscaper. The next day, I called Tom and told him what I found. "You know what?" he said, "Now that you mention it, many of our customers do refer to us as landscapers—'I need a landscaper. Call a landscaper' ". So, we started his campaign targeting the keyword landscaper. Because there was so little competition, he ranked in the top five in a matter of weeks and was number one in Google within two months. He was dominating half the search traffic within two months! The leads were rolling in so we switched to the keyword landscaping. It took longer—about three months—for him to break into the top ten. By that time, he had so many inquiries, he hardly even noticed. The lesson here is three-fold: You may know some of the keywords, however, that doesn't mean you know them all. Just because you think of yourself in one particular way doesn't mean your customers do. By taking the time to do keyword research, you will reveal opportunities in your market that you didn't know existed. What your keyword goal is Before you start looking at keywords, you need to fix your goal firmly in your mind. There are basically two major reasons to do SEO. Goal 1: Brand awareness This may come as a surprise but there are people out there who don't know that you exist. SEO is a powerful and inexpensive way to get your name out there and build some credibility with your target customers. There are three major types of brand awareness: Company brand awareness Company brand awareness works on getting the name of your company into the market. If you want to build credibility for Big Computers Unlimited as a whole, then you probably want a campaign focused on getting your company listed where other top producers of PCs are listed. PC, computer, or fast computer all might be good terms. Product brand awareness Product brand awareness focuses on building general market knowledge of one product or line of products that your company produces. If you work for Big Computers Unlimited and you want to sell more Intergalactic Gamer brand computers at retail stores throughout the country, then you probably want to build a campaign around keywords like Gaming PC or even high-end PC. Credibility A 2004 survey by iProspect found that two out of three search engine users believed that the highest search results in Google were the top brands for their industry; there is little reason to believe this perception has changed. That means that just by being at the top of Google will gain you a certain level of trust among search engine users. If Big Computers Unlimited can rank in the top three for Gaming PCs, they'll develop a lot of creed among gamers. Goal 2: Conversions Conversions are a fancy way of saying that the visitor did what you wanted them to do. There are three typical types of conversions: Transactional A transaction is just what it sounds like. Someone puts in a credit card and buys your product. This is typical of most product-focused web sites but isn't limited to this narrow category. Your web site may sell registrations to online training, subscriptions to magazines, or even credit monitoring. The bottom line is that it can be purchased on the site. You need to focus your keyword research on terms that will bring buyers who are ready to purchase right now. Words like buy, price, merchant, store, and shop indicate a desire for immediate purchase. Give them the transactional information they need like price, color choices, size, quantity discounts, return policy, and delivery options. With this information and a great checkout experience you'll have them buying from you in no time. UbercartUbercart is simply the best shopping cart solution for Drupal. If you're a transactional web site and you need an e-commerce solution, start here: http://www.ubercart.org/. Lead Generation If you're in an industry with a long sales cycle like real estate, legal services, or medical, then you're probably interested in generating leads rather than online transactions. If you sell a service or product that requires direct contact with your customer, like consulting or personal training, then you probably want leads too. Lead generation means that instead of buying from your web site, you're interested in someone expressing an interest in doing business with you so that you can follow up with them later. You let them express interest by filling out a contact form, emailing you, or even picking up the phone and calling you. You need to focus your keyword research on terms that will bring people who are perhaps a little earlier in the buying process. Words like review, compare, best, information, and generic names of your product indicate a user is researching but not quite ready to buy. You'll need to provide a lot of information on your web site to inform them and shape their thinking about your product. Page impression (or ad impression) Some web sites make money when visitors view an ad. To these sites, a conversion may simply be someone clicking on one or more pages so that they'll see one more ads. You need to focus your keyword research on terms that will bring people seeking information or news to your web site. Keyword research tools There are many tools to help you find the right keywords. It's not important that you use them all but you should try a few of them just so you can see what's out there. Here are my favorites in order of preference: Your own web site The most important keyword research tool at your disposal is your own web site, http://www.yourDrupalsite.com/. If you already have some traffic, chances are that they're coming from somewhere. With analytics installed, you should be able to find out what they're searching on with just a few clicks. If you have Google Analytics installed then you can easily see this data by logging in to its admin section and then going to Traffic Sources | Search Engines | Google. This information can be invaluable if you cross-reference it with your current positions in the search engines. Say, for example, that you're getting 100 searchers a month on a term that you're on page 2 of Google. That's a good indicator that people are searching hard to find a company like yours and that may be a very good term to focus on with your search engine campaign. If you're getting that much traffic on page 2, imagine if you were in the top three on page 1. Drupal has a built-in search engine—another great tool to see what the people are searching for, after they've already visited your site. There's an insanely useful module for that, called Top Searches (http://drupal.org/project/top_searches) that does a better job that Drupal's built-in list. This module was developed by the founder of Linnovate, Zohar Stolar. Thanks, Zohar!
Read more
  • 0
  • 0
  • 942
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 £15.99/month. Cancel anytime
article-image-developing-application-symfony-13-part-1
Packt
10 Nov 2009
5 min read
Save for later

Developing an Application in Symfony 1.3 (Part 1)

Packt
10 Nov 2009
5 min read
The milkshake shop Our application will be designed for a fictitious milkshake shop. The functional requirements for our shop are: Display of the menu, job vacancies, and locations; these details will be updated from the back office Sign up to a newsletter, where users can submit their details Search facility for the menu Secure backend for staff to update the site The site must be responsive Option for the users to view our vacancies in three languages Creating the skeleton folder structure Symfony has many ways in which it can help the developer create applications with less efforts—one way is by using the Symfony tasks available on the Command Line Interface (CLI). These Symfony tasks do the following: Generate the folder structure for your project, modules, applications, and tasks Clear the generated cache and rotate log files Create controllers to enable and disable an application and set permissions Interact with the ORM layer to build models and forms Interact with SQL to insert data into the database Generate initial tests and execute them Search for text in your templates that need i18N dictionaries and create them If you'd like to see all the tasks that are available with Symfony, just type the following on your command line:symfony Let's begin with using one of the Symfony tasks to generate the file structure for our project. Our project will reside in the milkshake folder. In your terminal window, change your folder path to the path that will hold your project and create this milkshake folder. My folder will reside in workspace folder. Therefore, I would type this: $mkdir ~/workspace/milkshake && cd ~/workspace/milkshake Now that I have the project folder and have changed the path to within the milkshake folder, let's use a Symfony task to generate the project file structure. In your terminal window, type the following: $/home/timmy/workspace/milkshake>symfony generate:project -orm=Propel milkshake We can generate our project we can also specify which ORM we would like to use. Our application is going to use the Propel ORM, but you can also opt for Doctrine ORM. By default, Doctrine ORM is enabled. After pressing the Enter key, the task goes into action. Now you will see output like the one in the following screenshot on your terminal window. This screenshot shows the folder structure being created: Let's have a look at the top-level folders that have been created in our project folder: Folders Description apps This folder contains our frontend application and any other applications that we will create batch If there are any batch scripts, they are placed here cache This folder is the location for cached files and/or scaffolded modules config This folder holds all the main configuration files and the database schema definitions data This folder holds data files such as test or fixture data doc All our documentation should be stored in this folder; this includes briefs, PhpDoc for API, Entity Relationship Diagrams, and so on lib Our models and classes are located in this folder so that all applications can access them log This folder holds the development controllers log files and our Apache log plugins All installed plugins are located in this folder test All unit and functional tests should be placed in this folder; Symfony will create and place stubs in this folder when we create our modules web This is the web root folder that contains all web resources such as images, CSS, JavaScript, and so on There are three folders that must be writable by the web server. These are the cache, log, and web/uploads/. If these are not writable to your web server, then the server itself will either produce warnings at startup or fail to start. The files permissions are usually set during the generation process, but sometimes this can fail. You can use the following task to set the permissions:$/home/timmy/workspace/milkshake>symfony project:permissions With the initial project skeleton built, next we must create an application. Symfony defines an application as operations that are grouped logically and that can run independent of one another. For example, many sites have a frontend and back office/admin area. The admin area is where a user logs in and updates areas on the frontend. Of course, our application will also provide this functionality. We will call these areas frontend and backend. Our first application is going to be the frontend. Again, let's use a Symfony task to generate the frontend application folder structure. Enter the following task in your terminal window: $/home/timmy/workspace/milkshake>symfony generate:app frontend Again, after executing this task, you will see the following in your terminal window: Let's have a look at the top-level folders that have just been created in the apps folder: Directory Description config This folder will have all configuration files for our application i18N This folder will contain all Internationalization files lib This folder will hold all application-specific classes modules All our modules will be built in this folder templates The default layout template is created in this folder, and any other global templates that will be created will also be placed in this folder These steps will generate our project and frontend application folder structure, and we can now view our project in a web browser. Open your web browser and go to http://milkshake/frontend_dev.php and you will see the following default Symfony page: Now we can see the default project page along with the instructions on what to do next. The frontend_dev.php file is our index file while developing the frontend application, and adheres to the naming convention of <application>_<environment>.php. This controller is the development controller and is rather helpful while developing. Before we continue though, I urge you to also have a look at the web debug toolbar.
Read more
  • 0
  • 0
  • 1687

article-image-developing-application-symfony-13-part-2
Packt
10 Nov 2009
7 min read
Save for later

Developing an Application in Symfony 1.3 (Part 2)

Packt
10 Nov 2009
7 min read
Building the database The last step is to create the database and then create all of the tables. I have created my database called milkshake on the CLI using the following command: $/home/timmy/workspace/milkshake>mysqladmin create milkshake -u root -p Now that we have created the database, we need to generate the SQL that will create our tables. Again, we are going to use a Symfony task for this. Just like creating the ORM layer, the task will build the SQL based on the schema.xml file. From the CLI, execute the following task: $/home/timmy/workspace/milkshake>symfony propel:build-sql This has now generated a SQL file that contains all of the SQL statements needed to build the tables in our database. This file is located in the data/sql folder within the project folder. Looking at the generated lib.model.schema.sql file in this folder, you can view the SQL. Next, we need to insert the SQL into the database. Again using a Symfony task, execute the following on the CLI: $/home/timmy/workspace/milkshake>symfony propel:insert-sql During the execution of the task, you will be prompted to enter a y or N as to whether you want to delete the existing data. As this command will delete your existing tables and then create new tables, enter y. During development, the confirmation can become tiring. To get around this you can append the no-confirmation switch to the end as shown here: >symfony propel:insert-sql --no-confirmation Afterwards, check in your database and you should see all of the tables created as shown in the following screenshot: I have showed you how to execute each of the tasks in order to build everything. But there is a simpler way to do this, and that is with yet another Symfony task which executes all of the above tasks: $/home/timmy/workspace/milkshake>symfony propel:build-all or $/home/timmy/workspace/milkshake>symfony propel:build-all --no-confirmation Our application is now all set up with a database and the ORM layer configured. Next, we can start on the application logic and produce a wireframe. Creating the application modules In Symfony, all requests are initially handled by a front controller before being passed to an action. The actions then implement the application logic before returning the presentation template that will be rendered. Our application will initially contain four areas—home, location, menu, and vacancies. These areas will essentially form modules within our frontend application. A module is similar to an application, which is the place to group all application logic and is self contained. Let's now create the modules on the CLI by executing the following tasks: $/home/timmy/workspace/milkshake>symfony generate:module frontend home$/home/timmy/workspace/milkshake>symfony generate:module frontend location$/home/timmy/workspace/milkshake>symfony generate:module frontend menu$/home/timmy/workspace/milkshake>symfony generate:module frontend vacancies Executing these tasks will create all of the modules' folder structures along with default actions, templates, and tests in our frontend application. You will see the following screenshot when running the first task: Let's examine the folder structure for a module: Folder Description actions This folder contains the actions class and components class for a module templates All modules templates are stored in this folder Now browse to http://milkshake/frontend_dev.php/menu and you will see Symfony's default page for our menu module. Notice that this page also provides useful information on what to do next. This information, of course, is to render our template rather than have Symfony forward the request. Handling the routing We have just tested our menu module and Symfony was able to handle this request without us having to set anything. This is because the URL was interpreted as http://milkshake/module/action/:params. If the action is missing, Symfony will automatically append index and execute the index action if one exists in the module. Looking at the URL for our menu module, we can use either http://milkshake/frontend_dev.php/menu or http://milkshake/frontend_dev.php/menu/index for the moment. Also, if you want to pass variables from the URL, then we can just add them to the end of the URL. For example, if we wanted to also pass page=1 to the menu module, the URL would be http://milkshake/frontend_dev.php/menu/index/page/1. The problem here is that we must also specify the name of the action, which doesn't leave much room for customizing a URL. Mapping the URL to the application logic is called routing. In the earlier example, we browsed to http://milkshake/frontend_dev.php/menu and Symfony was able to route that to our menu module without us having to configure anything. First, let's take a look at the routing file located at apps/frontend/config/routing.yml. # default ruleshomepage: URL: / param: { module: default, action: index }default_index: URL: /:module param: { action: index }default: URL: /:module/:action/* This is the default routing file that was generated for us. Using the home page routing rules as an example, the route is broken down into three parts: A unique label: homepage A URL pattern: URL: / An array of request parameters: param: { module: menu, action: index } We refer to each one of the rules within the routing file using a unique label. A URL pattern is what Symfony uses to map the URL to a rule, and the array of parameters is what maps the request to the module and the action. By using a routing file, Symfony caters for complicated URLs, which can restrict parameter types, request methods, and associate parameters to our Propel ORM layer. In fact, Symfony includes an entire framework that handles the routing for us. The application logic As we have seen, Symfony routes all requests to an action within a module. So let's open the actions class for our menu module, which is located at apps/frontend/modules/menu/actions/actions.class.php. class menuActions extends sfActions{ /** * Executes index action * * @param sfRequest $request A request object */ public function executeIndex(sfWebRequest $request) { $this->forward('default', 'module'); }} This menuActions class contains all of the menu actions and as you can see, it extends the sfActions base class. This class was generated for us along with a default 'index' action (method). The default index action simply forwards the request to Symfony's default module, which in turn generates the default page that we were presented with. All of the actions follow the same naming convention, that is, the action name must begin with the word execute followed by the action name starting with a capital letter. Also, the request object is passed to the action, which contains all of the parameters that are in the request. Let's begin by modifying the default behavior of the menu module to display our own template. Here we need the application logic to return the template name that needs to be rendered. To do this, we simply replace the call to the forward function with a return statement that has the template name: public function executeIndex(sfWebRequest $request) { return sfView::SUCCESS; } A default index template was also generated for us in the templates folder, that is, apps/frontend/modules/menu/templates/indexSuccess.php. Returning the sfView::SUCCESS constant will render this template for us. The template rendered will depend on the returned string from the action. All templates must also follow the naming convention of actionNameReturnString.php. Therefore, our action called index returns the sfView constant SUCCESS, meaning that the indexSuccess.php template needs to be present within the templates folder for our menu module. We can return other strings, such as these: return sfView::ERROR: Looks for the indexError.php template return myTemplate: Looks for the indexmyTemplate.php template return sfView::NONE: Will not return a template and, therefore, bypass the view layer; this could be used as an example for an AJAX request However, just removing the $this->forward('default', 'module') function will also return indexSuccess.php by default. It is worth adding the return value for ease in reading. Now that we have rendered the menu template, go ahead and do the same for the home, locations, and vacancies modules.
Read more
  • 0
  • 0
  • 1054

article-image-search-engine-optimization-using-sitemaps-drupal-6
Packt
10 Nov 2009
8 min read
Save for later

Search Engine Optimization using Sitemaps in Drupal 6

Packt
10 Nov 2009
8 min read
Let's get started. As smart as the Google spider is, it's possible for them to miss pages on your site. Maybe you've got an orphaned page that isn't in your navigation anymore. Or, perhaps you have moved a link to a piece of content so that it's not easily accessible. It's also possible that your site is so big that Google just can't crawl it all without completely pulling all your server's resources—not pretty! The solution is a sitemap. In the early 2000s, Google started supporting XML sitemaps. Soon after Yahoo came out with their own standard and other search engines started to follow suit. Fortunately, in 2006, Google, Yahoo, Microsoft, and a handful of smaller players all got together and decided to support the same sitemap specification. That made it much easier for site owners to make sure every page of their web site is crawled and added to the search engine index. They published their specification at http://sitemaps.org. Shortly thereafter, the Drupal community stepped up and created a module called (surprise!) the XML sitemap module. This module automatically generates an XML sitemap containing every node and taxonomy on your Drupal site. Actually, it was written by Matthew Loar as part of the Google Summer of Code. The Drupal 6 version of the module was developed by Kiam LaLuno. Finally, in mid-2009, Dave Reid began working on a version 2.0 of the module to address performance, scalability, and reliability issues. Thanks, guys! According to www.sitemaps.org: Sitemaps are an easy way for Webmasters to inform search engines about pages on their sites that are available for crawling. In its simplest form, a Sitemap is an XML file that lists URLs for a site along with additional metadata about each URL (when it was last updated, how often it usually changes, and how important it is, relative to other URLs in the site) so that search engines can more intelligently crawl the site. Web crawlers usually discover pages from links within the site and from other sites. Sitemaps supplement this data to allow crawlers that support Sitemaps to pick up all URLs in the Sitemap and learn about those URLs using the associated metadata. Using a sitemap does not guarantee that every page will be included in the search engines. Rather, it helps the search engine crawlers find more of your pages. In my experience, submitting an XML Sitemap to Google will greatly increase the number of pages when you do a site: search. The keyword site: searches show you how many pages of your site are included in the search engine index, as shown in the following screenshot: Setting up the XML Sitemap module The XML Sitemap module creates a sitemap that conforms to the sitemap.org specification. Which XML Sitemap module should you use? There are two versions of the XML Sitemap module for Drupal 6. The 1.x version is, as of this writing, considered the stable release and should be used for production sites. However, if you have a site with more than about 2000 nodes, you should probably consider using the 2.x version. From www.drupal.org: 'The 6.x-2.x branch is a complete refactoring with considerations for performance, scalability, and reliability. Once the 6.x-2.x branch is tested and upgradeable, the 6.x-1.x branch will no longer be supported'. What this means is that in the next few months (quite possibly by the time you're reading this) everyone should be using the 2.x version of this module. That's the beauty of open source software—there are always improvements coming that make your Drupal site better Search Engine Optimized. Carry out the following steps to set up the XML Sitemap module: Download the XML Sitemap module and install it just like a normal Drupal module.  When you go to turn on the module, you'll be presented with a list that looks similar to the following screenshot: Now that you have the XML sitemap module properly installed and configured, you can start defining the priority of the content on your site—by default, the priority is .5. However, there are times when you may want Google to visit some content more often and other times when you may not want your content in the sitemap at all (like the comment or contact us submission forms). Each node now has an XML sitemap section that looks like the following screenshot: Before you turn on any included modules, consider what pieces of content on your site you want to show up in the search engines and only turn on the modules you need. The XML sitemap module is required. Turn it on. XML sitemap custom allows you to add your own customized links to the sitemap. Turn it on. XML sitemap engines will automatically submit your sitemap to the search engines each time it changes. This is not necessary and there are better ways to submit your sitemap. However, it does a nice job of helping you verify your site with each search engine. Turn it on. XML sitemap menu adds your menu items to the sitemap. This is probably a good idea. Turn it on. XML sitemap node adds all your nodes. That's usually the bulk of your content so this is a must-have. Turn it on. XML sitemap taxonomy adds all your taxonomy term pages to the sitemap. Generally a good idea but some might not want this listed. Term pages are good category pages so I recommend it. Turn it on. Don't forget to click Save configuration. Go to http://www.yourDrupalsite.com/admin/settings/xmlsitemap or go to your admin screen and click on Administer | Site Configuration | XML sitemap link. You'll be able to see the XML sitemap, as shown in the following screenshot: Click on Settings and you'll see a few options, as shown in the following screenshot: Minimum sitemap lifetime: It determines that minimum amount of time that the module will wait before renewing the sitemap. Use this feature if you have an enormous sitemap that is taking too many server resources. Most sites should leave this set on No minimum. Include a stylesheet in the: The sitemaps will generate a simple css file to include with the sitemap that is generated. It's not necessary for the search engines but very helpful for troubleshooting or if any humans are going to view the sitemap. Leave it checked. Generate sitemaps for the following languages: In the future, this option will allow you to actually specify sitemaps for different languages. This is very important for international sites who want to show up in localized search engines. For now, English is the only option and should remain checked. Click the Advanced settings drop-down and you'll see several additional options. Number of links in each sitemap page allows you to specify how many links to pages on your web site will be in each sitemap. Leave it on Automatic unless you are having trouble with the search engines accepting the sitemap. Maximum number of sitemap links to process at once sets the number of additional links that the module will add to your sitemap each time the cron runs. This highlights one of the biggest differences between the new XML sitemap and the old one. The new sitemap only processes new nodes and updates the existing sitemap instead of reprocessing every time the sitemap is accessed. Leave this setting alone unless you notice that cron is timing out. Sitemap cache directory allows you to set where the sitemap data will be stored. This is data that is not shown to the search engines or users; it's only used by the module. Base URL is the base URL of your site and generally should be left as it is. Click on the Front page drop-down and set these options: Front page priority: 1.0 is the highest setting you can give a page in the XML sitemap. On most web sites, the front page is the single most important part of your site so, this setting should probably be left at 1.0. Front page change frequency: Tells the search engines how often they should revisit your front page. Adjust this setting to reflect how often the front page of your site changes. What is priority and how does it work? Priority is an often-misunderstood part of a sitemap. For instance, the priority is only used to compare pages of your own site and you cannot increase your ranking in the Search Engine Results Page (SERPS) by increasing the priority of your pages. However, it does help let the search engines know which pages of your site you feel are more important. They could use this information to select between two different pages on your site when deciding which page to show to a search engine user.
Read more
  • 0
  • 0
  • 1202

article-image-extending-search-engine-optimization-using-sitemaps-drupal-6
Packt
09 Nov 2009
5 min read
Save for later

Extending Search Engine Optimization using Sitemaps in Drupal 6

Packt
09 Nov 2009
5 min read
We have discussed some of the techniques earlier in Search Engine Optimization using Sitemaps in Drupal 6 article which mainly covered XML sitemaps. The Google spider is quite smart, but then the possibility of them missing pages on your site is also quite high. You may have pages that are not in navigation anymore or you have moved a link to a piece of content so that it's not easily accessible. A possibility is also that your site is very big for Google to just crawl it all without completely pulling all your server's resources. The solution to this is sitemaps. Google News XML Sitemap Google has created one of the most popular news sources on the Internet just by collecting and organizing news articles from other sites. It's called Google News and if you are running a news web site then you know how powerful it can be for picking up your comment. One front page article can generate 50,000 or more visitors in an hour or two. To be listed in Google News takes more than luck. You need to write great content and proactively seek to create timely and news-worthy content. If you've done that and you're still not showing up in Google news then it's time to create a Google News XML Sitemap. The Google News sitemap generator module was originally created by Adam Boyse at Webopius and is being maintained by Dave Reid. Thanks to both of you! Setting up the Google News sitemap generator module Carry out the following steps to set up the Google News sitemap generator module: Download the Google News sitemap module from http://drupal.org/project/googlenews and install it just like a normal Drupal module. Go to http://www.yourDrupalsite.com/admin/settings/googlenews or go to your admin screen, and click the Administer | Site Configuration | Google News sitemap feed link. You'll see a screen similar to the following screenshot: Select the content types that you wish to show up in the news feed. If all your story content types are newsworthy, pick Story. Your blog or page content types are probably not a good fit and selecting them may hurt the chances of your content being approved by Google. Click on Save configuration. Point your browser to http://www.yourDrupalsite.com/googlenews.xml and double check that you can see the sitemap, as shown in the following screenshot: Submitting your Google News sitemap to Google News Once you've assembled your new articles for a single publication label, submit them to Google News sitemaps by carrying out the following steps: Check Google News to see if your site is already included. If not, you can request inclusion by visiting the following link, http://www.google.com/support/news_pub/bin/request.py?ctx=answer. The inclusion process may take up to a few weeks, and you'll only be able to submit a News sitemap once this process is complete. If your site is already showing up in Google News then proceed. If not, you should wait a couple of weeks and try again. Log in to Google Webmaster Tools by pointing your browser to http://www.google.com/webmasters/tools/. On the Webmaster Tools Dashboard, click on Add next to the site you want to submit. From the Choose type drop-down menu, select News sitemap, and then type the sitemap URL, in this case http://www.yourDrupalsite.com/googlenews.xml. In the list, select the publication label for the articles. You can select only one label for each sitemap. Click on OK. URL list The XML Sitemap is the ideal choice because it allows you to specify a lot of information about the content of your site. But, say for some reason that you can't install an XML Sitemap. Maybe there's a conflict with another module that you just have to have. Perhaps your server doesn't have the power to handle the large overhead that an XML sitemap needs for large sites. Or, possibly you want to submit a sitemap to a search engine that doesn't support XML yet. Well, there is an alternative. It's not as robust but it is a functional, albeit rudimentary, solution. Just make a list of every URL in your site and put the list in one big text document with one URL on each line. Too much work, you say? Good thing there is a Drupal module that does all the work for you. It's called the URL list module. It's maintained by David K. Norman. Thank you, David! Setting up a URL list sitemap Download the Sitemap module from http://drupal.org/project/urllist and install it just like a normal Drupal module. Go to http://www.yourDrupalsite.com/admin/settings/urllist or go to your admin screen and click the Administer | Site Configuration | URL list link. You'll see the URL list screen, as shown in the following screenshot: You can adjust the settings to keep track of who accessed the URL list to submit your site to Yahoo! and to help you authenticate with Yahoo! However, you can leave all these settings untouched for now. Point your browser to http://www.yourDrupalsite.com/urllist.txt (http://www.yourDrupalsite.com/?q=urllist.txt if you don't have clean URLs installed) and you'll see your URL list sitemap, as shown in the following screenshot: You can submit this sitemap to Google, Yahoo!, and many other search engines in lieu of an XML sitemap. Just follow the same steps as defined in the Submit your XML Sitemap to Google section above but use # http://www.yourDrupalsite.com/urllist.txt as the URL. Remember to use http://www.yourDrupalsite.com/?q=urllist.txt if Google has problems with your URL.
Read more
  • 0
  • 0
  • 951
article-image-phx-place-holders-extended-modx
Packt
28 Oct 2009
4 min read
Save for later

PHx (Place Holders extended) in MODx

Packt
28 Oct 2009
4 min read
PHx in action Let us learn our need of PHx by building a new functionality for our site that lets us add profiles of our family members and friends. We will add a new page called 'Family and Friends' that will show the list of all the individuals that we add. Once the user clicks on an individual, it will show certain details such as name, relationship to you, occupation, web site. This is easy to implement; all we have to do is create the template variables for each of the fields, and create a template that uses these template variables. So, to display the Occupation, the template will have a code similar to the following: Occupation: [*occupation*] Though this might appear to work initially, it has a small glitch in it. When we are entering the personal details of an individual, we may not want to enter all of the values for every individual. In the case of not having a value for the variable, it looks cleaner to not show the label at all instead of leaving it blank. In our case—if we have no value for occupation—it will look cleaner to not show the label Occupation. So here comes a need for displaying certain text only if the template variable—in this case, occupation—has a value. We can do this using PHx without having to write a snippet. Installing PHx To download PHx, use the following steps: Download PHx from http://MODxcms.com/PHx-1006.html. Extract the contents of the downloaded file. Create a directory called phx in the assets/plugins folder of your MODx installation. Copy all the files within the extracted folder to the assets/plugins/phx folder. Create a new plug-in using the MODx Manager interface: Click on the Manage Resources menu item in the Resources menu Click on the Plugins tab Click on the New Plugin link Fill it with the following details: Field Name Field Value Plugin Name PHx Plugin Code Contents of the file phx.plugin.txt in the extracted folder System Events | OnParseDocument Checked      6.Click on Save. Adding Family and Friends documents Let us create a page that lists all the members from the Family or Friends group. This document will be a container that will have a document for each member that you would like to add. Hence, just as you have learned earlier, a call to the Ditto snippet can get you all the documents that a container holds. Create a page with the following details: Field Name Field Value Title Family and Friends Uses template Learning MODx default template Modify the page created in the previous step to have the following code. (Note that we need to know the ID of the created page for the code and hence, we are modifying it after creating it.) <ul> [!Ditto? &parents=`65` &tpl=`familyandfriendslist`!]</ul> Here, 65 is the ID of the created document. We give the ID of this document here as we will be adding the other documents as child documents of this document. In the above Ditto call, we have indicated that we are using a custom chunk to control the appearance of the listing. Create a chunk with the following details, to show a neat list of the documents that represent a member with the title and a link to the document. Field Name Field Value Chunk name familyandfriendslist Existing Category Learning MODx Chunk Code <li> <h3><a href="[~[+id+]~]">[+title+]</a></h3> </li>  
Read more
  • 0
  • 0
  • 1064

article-image-elgg-social-networking-installation
Packt
28 Oct 2009
14 min read
Save for later

Elgg Social Networking - Installation

Packt
28 Oct 2009
14 min read
Installing Elgg In addition to its impressive feature list, Elgg is an admin's dolly. In this tutorial by Mayank Sharma, we will see how Elgg can be installed in popular Linux web application rollout stack of Linux, Apache, MySQL, and PHP, fondly referred to as LAMP. As MySQL and PHP can run under Windows operating system as well, you can set up Elgg to serve your purpose for such an environment. Setting Up LAMP Let's look at setting up the Linux, Apache, MySQL, PHP web server environment. There are several reasons for the LAMP stack's popularity. While most people enjoy the freedom offered by these Open Source software, small business and non-profits will also be impressed by its procurement cost—$0. Step 1: Install Linux The critical difference between setting up Elgg under Windows or Linux is installing the operating system. The Linux distribution I'm using to set up Elgg is Ubuntu Linux ( http://www.ubuntu.com/ ).It's available as a free download and has a huge and active global community, should you run into any problems. Covering step-by-step installation of Ubuntu Linux is too much of a digression for this tutorial. Despite the fact that Ubuntu isn't too difficult to install, because of its popularity there are tons of installation and usage documentation available all over the Web. Linux.com has a set of videos that detail the procedure of installing Ubuntu ( http://www.linux.com/articles/114152 ).Ubuntu has a dedicated help section ( https://help.ubuntu.com/ ) for introduction and general usage of the distribution. Step 2: Install Apache Apache is the most popular web server used on the Internet. Reams and reams of documents have been written on installing Apache under Linux. Apache's documentation sub-project ( http://httpd.apache.org/docs-project/ ) has information on installing various versions of Apache under Linux. Ubuntu, based on another popular Linux distribution, Debian, uses a very powerful and user-friendly packaging system. It's called apt-get and can install an Apache server within minutes. All you have to do is open a terminal and write this command telling apt-get what to install: apt-get install apache2 apache2-common apache2-doc apache2-mpm-prefork apache2-utils libapr0 libexpat1 ssl-cert This will download Apache and its most essential libraries. Next, you need to enable some of Apache's most critical modules: a2enmod ssla2enmod rewritea2enmod include The rewrite module is critical to Elgg, so make sure it's enabled, else Elgg wouldn't work properly. That's it. Now, just restart Apache with: /etc/init.d/apache2 restart. Step 3: MySQL Installing MySQL isn't too much of an issue either. Again, like Ubuntu and Apache, MySQL can also boast of a strong and dedicated community. This means there's no dearth of MySQL installation or usage related documentation ( http://www.mysql.org/doc/ ). If you're using MySQL under Ubuntu, like me, installation is just a matter of giving apt-get a set of packages to install: apt-get install mysql-server mysql-client libmysqlclient12-dev Finally, set up a password for MySQL with: mysqladmin -h yourserver.example.com -u root password yourrootmysqlpassword Step 4: Install PHP Support You might think I am exaggerating things a little bit here, but I am not, PHP is one of the most popular and easy to learn languages for writing web applications. Why do you think we are setting up out Linux web server environment to execute PHP? It's because Elgg itself is written in PHP! And so are hundreds and thousands of other web applications. So I'm sure you've guessed by now that PHP has a good deal of documentation ( http://www.php.net/docs.php )as well. You've also guessed it's now time to call upon Ubuntu's apt-get package manager to set up PHP:  apt-get install libapache2-mod-php5 php5 php5-common php5-gd php5-mysql php5-mysqli As you can see, in addition to PHP, we are also installing packages that'll hook up PHP with the MySQL database and the Apache web server. That's all there is to setting up the LAMP architecture to power your Elgg network. Setting Up WAMP If you are used to Microsoft's Windows operating system or want to avoid the extra minor learning curve involved with setting up the web server on a Linux distribution, especially, if you haven't done it before, you can easily replicate the Apache, MySQL, PHP web server on a Windows machine. Cost wise, all server components the Apache web server, MySQL database, and the PHP development language have freely available Windows versions as well. But the base component of this stack, the operating system —Microsoft Windows, isn't. Versions of Apache, MySQL, and PHP for Windows are all available on the same websites mentioned above. As Windows doesn't have an apt-get kind of utility, you'll have to download and install all three components from their respective websites, but you have an easier way to set up a WAMP server. There are several pre-packaged Apache, MySQL, and PHP software bundles available for Windows(http://en.wikipedia.org/wiki/Comparison_of_WAMPs).I've successfully run Elgg on the WAMP5 bundle (http://www.en.wampserver.com/). The developer updates the bundle, time and again, to make sure it's running the latest versions of all server components included in the bundle. Note - While WAMP5 requires no configuration, make sure you have Apache's rewrite_module and PHP's php_gd2 extension enabled. They will have a bullet next to their name if they are enabled. If the bullet is missing, click on the respective entries under the Apache and PHP sub-categories and restart WAMP5. Installing Elgg Now that we have a platform ready for Elgg, let's move on to the most important step of setting up Elgg. Download the latest version of Elgg from its website. At the time of writing this tutorial, the latest version of Elgg was Elgg-0.8. Elgg is distributed as a zipped file. To uncompress under Linux: Move this zipped file to /tmp and uncompress it with the following command: $ unzip /tmp/elgg-0.8.zip To uncompress under Windows: Right-click on the ZIP file and select the Extract here option. After uncompressing the ZIP file, you should have a directory called Elgg-<version-number>, in my case, elgg-0.8/. This directory contains several sub directories and files. The INSTALL file contains detailed installation instructions. The first step is to move this uncompressed directory to your web server. Note: You can set up Elgg on your local web server that sits on the Internet or on a paid web server in a data center anywhere on the planet. The only difference between the two setups is that if you don't have access to the local web server, you'll have to contact the web service provider and ask him about the transfer options available to you. Most probably, you'll have FTP access to your web server, and you'll have to use one of the dozens of FTP clients, available for free, to transfer Elgg's files from your computer to the remote web server. Optionally, if you have "shell" access on the web server, you might want to save time by transferring just the zipped file and unzipping it on the web server itself. Contact your web server provider for this information. The web server's directory where you need to copy the contents of the Elgg directory depends upon your Apache installation and operating system. In Ubuntu Linux, the default web server directory is /var/www/. In Windows, WAMP5 asks where it should create this directory during installation. By default, it's the www directory and is created within the directory you installed WAMP5 under. Note: Another important decision you need to make while installing Elgg is how do you want your users to access your network. If you're setting up the network to be part of your existing web infrastructure, you'll need to install Elgg inside a directory. If, on the other hand, you are setting up a new site just for the Elgg-powered social network, copy the contents of the Elgg directory inside the www directory itself and not within a subdirectory. Once you have the Elgg directory within your web server's www directory, it's time to set things in motion. Start by renaming the config-dist.php file to config.php and the htaccess-dist to .htaccess. Simply right-click on the file and give it a new name or use the mv command in this format: $ mv <original-file-name> <new-file-name> Note : To rename htacces-dist to .htaccess in Windows, you'll have to open the htaccess-dist file in notepad and then go to File | Save As and specify the name as .htaccess with the quotes. Editing config.php Believe it or not, we've completed the "installation" bit of setting up Elgg. But we still need to configure it before throwing the doors open to visitors. Not surprisingly, all this involves is creating a database and editing the config.php file to our liking. Creating a Database Making an empty database in MySQL isn't difficult at all. Just enter the MySQL interactive shell using your username, password, and hostname you specified while installing MySQL. $ mysql -u root -h localhost -pEnter password: Welcome to the MySQL monitor. Commands end with ; or g.Your MySQL connection id is 9 to server version: 5.0.22-Debian_0ubuntu6.06.3-logType 'help;' or 'h' for help. Type 'c' to clear the buffer.mysql> CREATE DATABASE elgg; You can also create a MySQL database using a graphical front-end manager like PHPMyAdmin, which comes with WAMP5. Just look for a database field, enter a new name (Elgg), and hit the Create button to create an empty Elgg database. Initial Configuration Elgg has a front-end interface to set up config.php, but there are a couple of things you need to do before you can use that interface: Create a data directory outside your web server root. As described in the configuration file, this is a special directory where uploaded files will go. It's also advisable to create this directory outside your main Elgg install. This is because this directory will be writable by everyone accessing the Elgg site and having such a "world-accessible" directory under your Elgg installation is a security risk. If you call the directory Elgg-data, make it world-readable with the following command: $ chmod 777 elgg-data Setup admin username and password. Before you can access Elgg's configuration web front-end, you need an admin user and a password. For that open the config.php file in your favorite text editor and scroll down to the following variables: $CFG->adminuser = "";$CFG->adminpassword = ""; Specify your chosen admin username and password between the quotes, so that it looks something like this: $CFG->adminuser = "admin";$CFG->adminpassword = "765thyr3"; Make sure you don't forget the username and password of the admin user. Important Settings When you have created the data directory and specified an admin username and password, it's time to go ahead with the rest of the configuration. Open a web browser and point to http://<your-web-server>/<Elgg-installation>/elggadmin/ This will open up a simple web page with lots of fields. All fields have a title and a brief description of the kind of information you need to fill in that field. There are some drop-down lists as well, from which you have to select one of the listed options. Here are all the options and their descriptions: Administration panel username: Username to log in to this admin panel, in future, to change your settings. Admin password: Password to log in to this admin panel in future. Site name: Enter the name of your site here (e.g. Elgg, Apcala, University of Bogton's Social Network, etc.). Tagline: A tagline for your site (e.g. Social network for Bogton). Web root: External URL to the site (e.g. http://elgg.bogton.edu/). Elgg install root: Physical path to the files (e.g./home/Elggserver/httpdocs/). Elgg data root: This is a special directory where uploaded files will go. If possible, this should live outside your main Elgg installation? (you'll need to create it by hand). It must have world writable permissions set, and have a final slash at the end. Note: Even in Windows, where we use back slashes () to separate directories, use Unix's forward slashes (/) to specify the path to the install root, data root, and other path names. For example, if you have Elgg files under WAMP's default directory in your C drive, use this path: C:/wamp/www/elgg/. Database type: Acceptable values are mysql and postgres - MySQL is highly recommended. System administrator email: The email address your site will send emails from(e.g. [email protected]). News account initial password: The initial password for the 'news' account. This will be the first administrator user within your system, and you should change the password immediately after the first time you log in. Default locale: Country code to set language to if you have gettext installed. Public registration: Can general members of the public register for this system? Public invitations: Can users of this system invite other users? Maximum users: The maximum number of users in your system. If you set this to 0, you will have an unlimited number of users. Maximum disk space: The maximum disk space taken up by all uploaded files. Disable public comments: Set the following to true to force users to log in before they can post comments, overriding the per-user option. This is a handy sledgehammer-to-crack-a-nut tactic to protect against comment spam (although an Akismet plug-in is available from elgg.org). Email filter: Anything you enter here must be present in the email address of anyone who registers; e.g. @mycompany.com will only allow email address from mycompany.com to register. Default access: The default access level for all new items in the system. Disable user templates: If this is set, users can only choose from available templates rather than defining their own. Persistent connections: Should Elgg use persistent database connections? Debug: Set this to 2047 to get ADOdb error handling. RSS posts maximum age: Number of days for which to keep incoming RSS feed entries before deleting them. Set this to 0 if you don't want RSS posts to be removed. Community create flag: Set this to admin if you would like to restrict the ability to create communities to admin users. cURL path: Set this to the cURL executable if cURL is installed; otherwise leave blank. Note : According to Wikipedia, cURL is a command line tool for transferring files with URL syntax, supporting FTP, FTPS, HTTP, HTTPS, TFTP, SCP,SFTP, Telnet, DICT, FILE, and LDAP. The main purpose and use for cURL is to automate unattended file transfers or sequences of operations. For example, it is a good tool for simulating a user's actions at a web browser. Under Ubuntu Linux, you can install curl using the following command: apt-get install curl Templates location: The full path of your Default_Template directory. Profile location: The full path to your profile configuration file (usually, it's best to leave this in mod/profile/). Finally, when you're done, click on the Save button to save the settings. Note : The next version of Elgg, Elgg 0.9, will further simplify installation. Already an early release candidate of this version (elgg-0.9rc1) is a lot more straightforward to install and configure, for initial use. First Log In Now, it's time to let Elgg use these settings and set things up for you. Just point your browser to your main Elgg installation (http://<your-web-servergt;/<Elgg-installation>). It'll connect to the MySQL database and create some tables, then upload some basic data, before taking you to the main page. On the main page, you can use the news account and the password you specified for this account during configuration to log in to your Elgg installation.
Read more
  • 0
  • 0
  • 13347

article-image-overview-cherrypy-web-application-server-part2
Packt
27 Oct 2009
5 min read
Save for later

Overview of CherryPy - A Web Application Server (Part2)

Packt
27 Oct 2009
5 min read
Library CherryPy comes with a set of modules covering common tasks when building a web application such as session management, static resource service, encoding handling, or basic caching. The Autoreload Feature CherryPy is a long-running Python process, meaning that if we modify a Python module of the application, it will not be propagated in the existing process. Since stopping and restarting the server manually can be a tedious task, the CherryPy team has included an autoreload module that restarts the process as soon as it detects a modification to a Python module imported by the application. This feature is handled via configuration settings. If you need the autoreload module to be enabled while in production you will set it up as below. Note the engine.autoreload_frequency option that sets the number of seconds the autoreloader engine has to wait before checking for new changes. It defaults to one second if not present. [global]server.environment = "production"engine.autoreload_on = Trueengine.autoreload_frequency = 5 Autoreload is not properly a module but we mention it here as it is a common feature offered by the library. The Caching Module Caching is an important side of any web application as it reduces the load and stress of the different servers in action—HTTP, application, and database servers. In spite of being highly correlated to the application itself, generic caching tools such as the ones provided by this module can help in achieving decent improvements in your application's performance. The CherryPy caching module works at the HTTP server level in the sense that it will cache the generated output to be sent to the user agent and will retrieve a cached resource based on a predefined key, which defaults to the complete URL leading to that resource. The cache is held in the server memory and is therefore lost when stopping it. Note that you can also pass your own caching class to handle the underlying process differently while keeping the same high-level interface. The Coverage Module When building an application it is often beneficial to understand the path taken by the application based on the input it processes. This helps to determine potential bottlenecks and also see if the application runs as expected. The coverage module provided by CherryPy does this and provides a friendly browseable output showing the lines of code executed during the run. The module is one of the few that rely on a third-party package to run. The Encoding/Decoding Module Publishing over the Web means dealing with the multitude of existing character encoding. To one extreme you may only publish your own content using US-ASCII without asking for readers' feedback and to the other extreme you may release an application such as bulletin board that will handle any kind of charset. To help in this task CherryPy provides an encoding/decoding module that filters the input and output content based on server or user-agent settings. The HTTP Module This module offers a set of classes and functions to handle HTTP headers and entities. For example, to parse the HTTP request line and query string: s = 'GET /note/1 HTTP/1.1' # no query stringr = http.parse_request_line(s) # r is now ('GET', '/note/1', '','HTTP/1.1')s = 'GET /note?id=1 HTTP/1.1' # query string is id=1r = http.parse_request_line(s) # r is now ('GET', '/note', 'id=1','HTTP/1.1')http.parseQueryString(r[2]) # returns {'id': '1'}Provide a clean interface to HTTP headers:For example, say you have the following Accept header value:accept_value = "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"values = http.header_elements('accept', accept_value)print values[0].value, values[0].qvalue # will print text/html 1.0 The Httpauth Module This module provides an implementation of the basic and digest authentication algorithm as defined in RFC 2617. The Profiler Module This module features an interface to conduct a performance check of the application. The Sessions Module The Web is built on top of a stateless protocol, HTTP, which means that requests are independent of each other. In spite of that, a user can navigate an e-commerce website with the impression that the application more or less follows the way he or she would call the store to pass an order. The session mechanism was therefore brought to the Web to allow servers to keep track of users' information. CherryPy's session module offers a straightforward interface to the application developer to store, retrieve, amend, and delete chunks of data from a session object. CherryPy comes natively with three different back-end storages for session objects: Back-end type Advantages Drawbacks RAM Efficient Accepts any type of objects No configuration needed Information lost when server is shutdown Memory consumption can grow fast File system Persistence of the information Simple setup File system locking can be inefficient Only serializable (via the pickle module) objects can be stored Relational database (PostgreSQL built-in support) Persistence of the information Robust Scalable Can be load balanced Only serializable objects can be stored Setup less straightforward
Read more
  • 0
  • 0
  • 1827
article-image-overview-cherrypy-web-application-server-part1
Packt
27 Oct 2009
6 min read
Save for later

Overview of CherryPy - A Web Application Server (Part1)

Packt
27 Oct 2009
6 min read
Vocabulary In order to avoid misunderstandings, we need to define a few key words that will be used. Keyword Definition Web server A web server is the interface dealing with the HTTP protocol. Its goal is to transform incoming HTTP requests into entities that are then passed to the application server and also transform information from the application server back into HTTP responses. Application An application is a piece of software that takes a unit of information, applies business logic to it, and returns a processed unit of information. Application server An application server is the component hosting one or more applications. Web application server A web application server is simply the aggregation of a web server and an application server into a single component. CherryPy is a web application server. Basic Example To illustrate the CherryPy library we will go through a very basic web application allowing a user to leave a note on the main page through an HTML form. The notes will be stacked and be rendered in a reverse order of their creation date. We will use a session object to store the name of the author of the note. Each note will have a URI attached to itself, of the form /note/id. Create a blank file named note.py and copy the following source code. #!/usr/bin/python# -*- coding: utf-8 -*# Python standard library importsimport os.pathimport time################################################################The unique module to be imported to use cherrypy###############################################################import cherrypy# CherryPy needs an absolute path when dealing with static data_curdir = os.path.join(os.getcwd(), os.path.dirname(__file__))################################################################ We will keep our notes into a global list# Please not that it is hazardous to use a simple list here# since we will run the application in a multi-threaded environment# which will not protect the access to this list# In a more realistic application we would need either to use a# thread safe object or to manually protect from concurrent access# to this list###############################################################_notes = []################################################################ A few HTML templates###############################################################_header = """<html><head><title>Random notes</<title><link rel="stylesheet" type="text/css" href="/style.css"></link></head><body><div class="container">"""_footer = """</div></body></html>"""_note_form = """<div class="form"><form method="post" action="post" class="form"><input type="text" value="Your note here..." name="text"size="60"></input><input type="submit" value="Add"></input></form></div>"""_author_form = """<div class="form"><form method="post" action="set"><input type="text" name="name"></input><input type="submit" value="Switch"></input></form></div>"""_note_view = """<br /><div>%s<div class="info">%s - %s <a href="/note/%d">(%d)</a></div></div>"""################################################################ Our only domain object (sometimes referred as to a Model)###############################################################class Note(object):def __init__(self, author, note):self.id = Noneself.author = authorself.note = noteself.timestamp = time.gmtime(time.time())def __str__(self):return self.note################################################################ The main entry point of the Note application###############################################################class NoteApp:"""The base application which will be hosted by CherryPy"""# Here we tell CherryPy we will enable the session# from this level of the tree of published objects# as well as its sub-levels_cp_config = { 'tools.sessions.on': True }def _render_note(self, note):"""Helper to render a note into HTML"""return _note_view % (note, note.author,time.strftime("%a, %d %b %Y %H:%M:%S",note.timestamp),note.id, note.id)@cherrypy.exposedef index(self):# Retrieve the author stored in the current session# None if not definedauthor = cherrypy.session.get('author', None)page = [_header]if author:page.append("""<div><span>Hello %s, please leave us a note.<a href="author">Switch identity</a>.</span></div>"""%(author,))page.append(_note_form)else:page.append("""<div><a href="author">Set youridentity</a></span></div>""")notes = _notes[:]notes.reverse()for note in notes:page.append(self._render_note(note))page.append(_footer)# Returns to the CherryPy server the page to renderreturn [email protected] note(self, id):# Retrieve the note attached to the given idtry:note = _notes[int(id)]except:# If the ID was not valid, let's tell the# client we did not find itraise cherrypy.NotFoundreturn [_header, self._render_note(note), _footer]@cherrypy.exposedef post(self, text):author = cherrypy.session.get('author', None)# Here if the author was not in the session# we redirect the client to the author formif not author:raise cherrypy.HTTPRedirect('/author')note = Note(author, text)_notes.append(note)note.id = _notes.index(note)raise cherrypy.HTTPRedirect('/')class Author(object):@cherrypy.exposedef index(self):return [_header, _author_form, _footer]@cherrypy.exposedef set(self, name):cherrypy.session['author'] = namereturn [_header, """Hi %s. You can now leave <a href="/" title="Home">notes</a>.""" % (name,), _footer]if __name__ == '__main__':# Define the global configuration settings of CherryPyglobal_conf = {'global': { 'engine.autoreload.on': False,'server.socket_host': 'localhost','server.socket_port': 8080,}}application_conf = {'/style.css': {'tools.staticfile.on': True,'tools.staticfile.filename': os.path.join(_curdir,'style.css'),}}# Update the global CherryPy configurationcherrypy.config.update(global_conf)# Create an instance of the applicationnote_app = NoteApp()# attach an instance of the Author class to the main applicationnote_app.author = Author()# mount the application on the '/' base pathcherrypy.tree.mount(note_app, '/', config = application_conf)# Start the CherryPy HTTP servercherrypy.server.quickstart()# Start the CherryPy enginecherrypy.engine.start() Following is the CSS which should be saved in a file named style.css and stored in the same directory as note.py. html, body {background-color: #DEDEDE;padding: 0px;marging: 0px;height: 100%;}.container {border-color: #A1A1A1;border-style: solid;border-width: 1px;background-color: #FFF;margin: 10px 150px 10px 150px;height: 100%;}a:link {text-decoration: none;color: #A1A1A1;}a:visited {text-decoration: none;color: #A1A1A1;}a:hover {text-decoration: underline;}input {border: 1px solid #A1A1A1;}.form {margin: 5px 5px 5px 5px;}.info {font-size: 70%;color: #A1A1A1;} In the rest of this article we will refer to the application to explain CherryPy's design.
Read more
  • 0
  • 0
  • 2952

article-image-customizing-elgg-themes
Packt
27 Oct 2009
8 min read
Save for later

Customizing Elgg Themes

Packt
27 Oct 2009
8 min read
Why Customize? Elgg ships with a professional looking and slick grayish-blue default theme. Depending on how you want to use Elgg, you'd like your network to have a personalized look. If you are using the network for your own personal use, you really don't care a lot about how it looks. But if you are using the network as part of your existing online infrastructure, would you really want it to look like every other default Elgg installation on the planet? Of course not! Visitors to your network should easily identify your network and relate it to you. But theming isn't just about glitter. If you thought themes were all about gloss, think again. A theme helps you brand your network. As the underlying technology is the same, a theme is what really separates your network from the others out there. What Makes Up a Theme? There are several components that define your network. Let's take a look at the main components and some practices to follow while using them. Colors: Colors are an important part of your website's identity. If you have an existing website, you'd want your Elgg network to have the same family of colors as your main website. If the two (your website and social network) are very different, the changeover from one to another could be jarring. While this isn't necessary, maintaining color consistency is a good practice. Graphics: Graphics help you brand the network to make it your own. Every institution has a logo. Using a logo in your Elgg theme is probably the most basic change you'd want to make. But make sure the logo blends in with the theme, that is, it has the same background color. Code: It takes a little bit of HTML, a sprinkle of PHP, and some CSS magic to Code: It takes a little bit of HTML, a sprinkle of PHP, and some CSS magic to manipulate and control a theme. A CSS file: As the name suggests, this file contains all the CSS decorations. You can choose to alter colors and fonts and other elements in this file. A Pageshell file: In this pageshell file, you define the structure of your Elgg network. If you want to change the position of the Search bar or instead of the standard two-column format, move to a three-column display, this is the file you need to modify. Front page files: Two files control how the landing page of your Elgg network appears to logged out or logged in users. Optional images folder: This folder houses all the logos and other artwork that'll be directly used by the theme. Please note that this folder does not include any other graphic elements we've covered in previous tutorials such as your picture, or icons to communities, and so on. Controlling Themes Rather than being single humongous files, themes in Elgg are a bunch of small manageable files. The CSS decoration is separated from the placement code. Before getting our hands dirty creating a theme, let's take a look at the files that control the look and feel of your network. All themes must have these files: The Default Template Elgg ships with a default template that you can find under your Elgg installation. This is the structure of the files and folders that make up the default template. Before we look at the individual files and examine their contents in detail, let's first understand their content in general. All three files, pageshell, frontpage_logedin, and frontpage_loggedout are made up of two types of components. Keywords are used to pull content from the database and display them on the page. Arranging these keywords are the div and span tags along with several others like h1, ul, and so on that have been defined in the CSS file. What are <div> and <span>? The <div> and <span> are two very important tags especially when it comes to handling CSS files. In a snap, these two tags are used to style arbitrary sections of HTML. <div> does much the same thing as a paragraph tag <p>, but it divides the page up into larger sections. With <div>, you can also define the style of whole sections of HTML. This is especially useful when you want to give particular sections a different style from the surrounding text. The <span> tag is similar to the <div> tag. It is also used to change the style of the text it encloses. The difference between <span> and <div> is that a span element is in-line and usually used for a small chunk of in-line HTML. Both <div> and <span> work with two important attributes, class and id. The most common use of these containers together with the class or id attributes is when this is done with CSS to apply layout, color, and other presentation attributes to the page's content. In forthcoming sections, we'll see how the two container items use their two attributes to influence themes. The pageshell Now, let's dive into understanding the themes. Here's an exact replica of the pageshell of the Default template. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html > <head><title>{{title}}</title> {{metatags}}</head> <body>{{toolbar}} <div id="container"><!-- open container which wraps the header, maincontent and sidebar --><div id="header"><!-- open div header --><div id="header-inner"> <div id="logo"><!-- open div logo --> <h1><a href="{{url}}">{{sitename}}</a></h1> <h2>{{tagline}}</h2> </div><!-- close div logo --> {{searchbox}}</div> </div><!-- close div header --> <div id="content-holder"><!-- open content-holder div --><div id="content-holder-inner"> <div id="splitpane-sidebar"><!-- open splitpane-sidebar div --> <ul><!-- open sidebar lists --> {{sidebar}} </ul><!-- close sidebar lists --></div><!-- close splitpane-sidebar div --> <div id="splitpane-content"><!-- open splitpane-content div --> {{messageshell}} {{mainbody}}</div><!-- close open splitpane-content div --></div> </div><!-- close content-holder div --> <div id="footer"><!-- start footer --><div id="footer-inner"><span style="color:#FF934B">{{sitename}}</span> <a href="{{url}}content/terms.php">Terms and conditions</a> | <a href="{{url}}content/privacy.php">Privacy Policy</a><br /><a href="http://elgg.org/"><img src="{{url}}mod/template/ icons/elgg_powered.png" title="Elgg powered" border="0" alt="Elgg powered"/></a><br /> {{perf}}</div> </div><!-- end footer --> </div><!-- close container div --> </body> </html> CSS Elements in the pageshell Phew! That's a lot of mumbo-jumbo. But wait a second! Don't jump to a conclusion! Browse through this section, where we disassemble the file into easy-to-understand chunks. First, we'll go over the elements that control the layout of the pageshell. <div id="container">: This container wraps the complete page and all its elements, including the header, main content, and sidebar. In the CSS file, this is defined as: div#container {width:940px;margin:0 auto;padding:0;background:#fff;border-top:1px solid #fff;} <div id="header">: This houses all the header content including the logo and search box. The CSS definition for the header element: div#header {margin:0;padding:0;text-align:left;background:url({{url}}mod/template/templates/Default_Template/images/header-bg.gif) repeat-x;position:relative;width:100%;height:120px;} The CSS definition for the logo: div#header #logo{margin: 0px;padding:10px;float:left;} The search box is controlled by the search-header element: div#header #search-header {float:right;background:url({{url}}mod/template/templates/Default_Template/images/search_icon.gif) no-repeat left top;width:330px;margin:0;padding:0;position:absolute;top:10px;right:0;} <div id="header-inner">: While the CSS file of the default template doesn't define the header-inner element, you can use it to control the area allowed to the elements in the header. When this element isn't defined, the logo and search box take up the full area of the header. But if you want padding in the header around all the elements it houses, specify that using this element. <div id="content-holder">: This wraps the main content area. #content-holder {padding:0px;margin:0px;width:100%;min-height:500px;overflow:hidden;position:relative;} <div id="splitpane-sidebar">: In the default theme, the main content area has a two-column layout, split between the content and the sidebar area. div#splitpane-sidebar {width: 220px;margin:0;padding:0;background:#fff url({{url}}mod/template/templates/Default_Template/images/side-back.gif) repeat-y;margin:0;float: right;}div#splitpane-content {margin: 0;padding: 0 0 0 10px;width:690px;text-align: left;color:#000;overflow:hidden;min-height:500px;} <div id="single-page">: While not used in the Default template, the content area can also have a simple single page layout, without the sidebar. div#single-page {margin: 0;padding: 0 15px 0 0;width:900px;text-align: left;border:1px solid #eee;} <div id="content-holder-inner">: Just like header-inner, is used only if you would like a full page layout but a defined width for the actual content. <div id="footer">: Wraps the footer of the page including the links to the terms and conditions and the privacy policy, along with the Elgg powered icon. div#footer {clear: both;position: relative;background:url({{url}}mod/template/templates/Default_Template/images/footer.gif) repeat-x top;text-align: center;padding:10px 0 0 0;font-size:1em;height:80px;margin:0;color:#fff;width:100%;} <div id="footer-inner">: Like the other inner elements, this is only used if you would like a full page layout but restrict the width for the actual footer content.
Read more
  • 0
  • 74
  • 92343