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 - CMS & E-Commerce

830 Articles
article-image-building-user-portal-sermyadmin-openser
Packt
27 Oct 2009
8 min read
Save for later

Building the User Portal with SerMyAdmin for OpenSER

Packt
27 Oct 2009
8 min read
SerMyAdmin Originally, this material was written for SerWeb. SerWeb was originally developed for the SER project. Unfortunately, SerWeb became incompatible with newer versions of OpenSER. Another important aspect of SerWeb to be considered is its vulnerabilities. There are very few options for web interfaces to OpenSER. One of the tools we have found is OpenSER administrator. This tool is being developed using Ruby on Rails. While it seems to be a very good tool to administer an OpenSER server, it does not permit to provisioning users in the same way that SerWeb did and it lacks multi-domain support. OpenSER administrator can be found at http://sourceforge.net/projects/openseradmin. Since a tool to build an OpenSER portal was not available , we decided to build our own tool named SerMyAdmin using Java. After a slow start, it is now ready and we are using it to build a book. It is licensed according to GPLv2 and developed in Grails (Groovy on rails). It can be downloaded at http://sourceforge.net/projects/sermyadmin. What you are seeing here is the standalone tool. In our roadmap, we intend to integrate SerMyAdmin into the Liferay portal. Using a content management system such as Liferay (www.liferay.com) will make your task of building a portal much easier than it is today. The SerMyAdmin project can be found at sermyadmin.sourceforge.net. The idea is to facilitate the administration of the OpenSER database. SerMyAdmin is licensed under the GPLv2. Lab—Installing SerMyAdmin SerMyAdmin uses the Grails framework, so it needs an application server. You can choose from many application servers, such as IBM WebSphere, JBoss, Jetty, Tomcat, and so on. In this article we will use Apache Tomcat, because it's free and easy to install. Because we use some Java 1.5 features, we'll need Sun's Java JDK, not the free alternative GCJ. Step 1: Create an administrator for SerMyAdmin: mysql –u rootuse openserINSERT INTO 'subscriber' ( 'id' , 'username' , 'domain' , 'password' , 'first_name' , 'last_name' , 'email_address' , 'datetime_created' , 'ha1' , 'ha1b' , 'timezone' , 'rpid' , 'version' , 'password_hash' , 'auth_username' , 'class' , 'domain_id' , 'role_id' )VALUES (NULL , 'admin', 'openser.org', 'senha', 'Admin', 'Admin', '[email protected]', '0000-00-00 00:00:00', '1', '1', '1', '1', '1', NULL , '[email protected]', NULL , '1', '3'); Step 2: The next step we will take is to update our source's list to use the contrib repository and non-free packages. Our /etc/apt/sources.list, should look like below: # /etc/apt/souces.listdeb http://ftp.br.debian.org/debian/ etch main contrib non-freedeb-src http://ftp.br.debian.org/debian/ etch main contrib non-freedeb http://security.debian.org/ etch/updates main contrib non-freedeb-src http://security.debian.org/ etch/updates main contrib non-free/etc/apt/sources.list Notice that we have added only the keywords contrib and non-free after our repository definitions. Step 3: Update the package listing using the following command: openser:~# apt-get update Step 4: Install Sun's Java 1.5, running the command below: openser:~# apt-get install sun-java5-jdk Step 5: Make sure you are using Sun's Java. Please, run the command below to tell Debian that you want to use Sun's Java as your default Java implementation. openser:~# update-java-alternatives -s java-1.5.0-sun Step 6: If everything has gone well so far, you should run the following command and get a similar output. openser:~# java -version java version "1.5.0_14" Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_14-b03) Java HotSpot(TM) Client VM (build 1.5.0_14-b03, mixed mode, sharing) Step 7: Install Tomcat. You can obtain Tomcat at: http://tomcat.apache.org/download-60.cgi. To install Tomcat, just run the commands below: openser:/usr/local/etc/openser# cd /usr/localopenser:/usr/local# wget http://mirrors.uol.com.br/pub/apache/tomcat/tomcat-6/v6.0.16/bin/apache-tomcat-6.0.16.tar.gzopenser:/usr/local# tar zxvf apache-tomcat-6.0.16.tar.gzopenser:/usr/local# ln -s apache-tomcat-6.0.16 tomcat6 Step 8: To start Tomcat on your server initialization, please copy the following script to /etc/init.d/tomcat6. #! /bin/bash –e#### BEGIN INIT INFO# Provides: Apache’s Tomcat 6.0# Required-Start: $local_fs $remote_fs $network# Required-Stop: $local_fs $remote_fs $network# Default-Start: 2 3 4 5# Default-Stop: S 0 1 6# Short-Description: Tomcat 6.0 Servlet engine# Description: Apache’s Tomcat Servlet Engine### END INIT INFO## Author: Guilherme Loch Góes <[email protected]>#set -ePATH=/bin:/usr/bin:/sbin:/usr/sbin:CATALINA_HOME=/usr/local/tomcat6CATALINA_BIN=$CATALINA_HOME/bintest -x $DAEMON || exit 0. /lib/lsb/init-functionscase "$1" in start) echo "Starting Tomcat 6" "Tomcat6" $CATALINA_BIN/startup.sh log_end_msg $? ;; stop) echo "Stopping Tomcat6" "Tomcat6" $CATALINA_BIN/shutdown.sh log_end_msg $? ;; force-reload|restart) $0 stop $0 start ;; *) echo "Usage: /etc/init.d/tomcat6 {start|stop|restart}" exit 1 ;;esacexit 0 Step 9: Instruct Debian to run your script on startup; we do this with the command below. openser: chmod 755 /etc/init.d/tomcat6 openser:/etc/init.d# update-rc.d tomcat6 defaults 99 Step 10: To make sure everything is running correctly, reboot the server and try to open in your browser the URL http://localhost:8080; if everything is OK you'll be greeted with Tomcat's start page. Step 11: Install the MySQL driver for Tomcat, so that SerMyAdmin can access your database. This driver can be found at http://dev.mysql.com/downloads/connector/j/5.1.html. You should download the driver and unpack it, then copy the connector to Tomcat's shared library directory, as follows. openser:/usr/src# tar zxf mysql-connector-java-5.1.5.tar.gz openser:/usr/src# cp mysql-connector-java-5.1.5/mysql-connector-java-5.1.5-bin.jar /usr/local/tomcat6/lib Step 12: Declare the data source for SerMyAdmin to connect to OpenSER's database. You can do this in an XML file found at /usr/local/tomcat6/conf/context.xml. The file should look as below: <?xml version="1.0" encoding="UTF-8"?><Context path="/serMyAdmin"> <Resource auth="Container" driverClassName="com.mysql.jdbc.Driver" maxActive="20" maxIdle="10" maxWait="-1" name="jdbc/openser_MySQL" type="javax.sql.DataSource" url="jdbc:mysql://localhost:3306/openser" username="sermyadmin" password="secret"/></Context> In the file above, please change the highlighted parameters according to your scenario. SerMyAdmin can be installed in a different server than the one that holds the database. Do this for better scalability when possible. The default MySQL installation on Debian only accepts requests from localhost, so you should edit the file /etc/mysql/my.cnf, for MySQL to accept requests from external hosts. Step 13: Create a user to be referenced in the file context.xml. This user will have the required access to the database. Please, run the commands below: openser:/var/lib/tomcat5.5/conf# mysql -u root –p Enter password: Welcome to the MySQL monitor. Commands end with ; or g. Your MySQL connection id is 14 Server version: 5.0.32-Debian_7etch5-log Debian etch distribution Type 'help;' or 'h' for help. Type 'c' to clear the buffer. mysql> grant all privileges on openser.* to sermyadmin@'%' identified by 'secret'; Query OK, 0 rows affected (0.00 sec) Step 14: We're almost there. The next step is to deploy the SerMyAdmin WAR file. Please, download and copy the file serMyAdmin.war to Tomcat's webapps directory. Restart it, to activate the changes. openser:/usr/src# cp serMyAdmin-0.4.war /usr/local/tomcat6/webapps/serMyAdmin.war openser:/usr/src# invoke-rc.d tomcat6 restart Don't worry about database modifications; SerMyAdmin will automatically handle that for you. Step 15: Configure Debian's MTA (Message Transfer Agent) to allow SerMyAdmin to send a confirmation email to new users. Run the command below to configure Exim4 (default MTA for Debian). Ask your company's email administrator. openser:/# apt-get install exim4 openser:/# dpkg-reconfigure exim4-config You will be greeted with a dialog-based configuration menu; on this menu it's import to pay attention to two options: General type of mail configuration, which should be set to Internet Site so that we can send and receive mails directly using SMTP, and Domains to relay mail for, which should be set to the domain from which you want the emails from SerMyAdmin to appear to come. Step 16: Customize the file /usr/local/apache-tomcat-6.0.16/webapps/serMyAdmin-0.3/WEB-INF/spring/resource.xml, which contains the parameters that specify which email server is used to send mails and from whom these emails should appear to come from. The following is an example of this file: <?xml version="1.0" encoding="UTF-8"?><beans xsi_schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd"> <bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl"> <property name="host"><value>localhost</value></property> </bean> <!-- You can set default email bean properties here, eg: from/to/subject --> <bean id="mailMessage" class="org.springframework.mail.SimpleMailMessage"> <property name="from"><value>[email protected]</value></property> </bean></beans> The first parameter to change is the server that we will use to send emails. The second is the parameter specifying from whom those emails will appear to come. Restart Tomcat again and we're ready to go. When you point your browser to http://<server address>:8080/serMyAdmin you should be greeted with the login page, the same as we have shown at the start on this article.
Read more
  • 0
  • 0
  • 1539

article-image-aspnet-35-cms-adding-security-and-membership-part-1
Packt
27 Oct 2009
10 min read
Save for later

ASP.NET 3.5 CMS: Adding Security and Membership (Part 1)

Packt
27 Oct 2009
10 min read
Security is a concern in any web application, but the security this article deals with is that of user accounts, membership, and roles. We'll be using the ASP.NET membership and roles functions to allow certain users such as administrators to perform specific tasks. These tasks may include managing the application, while other users such as content editors, may be restricted to the specific tasks we want them to manage such as adding or changing content. User account management can be handled either by the application (in our case, our Content Management System) or by Windows itself, using standard Windows authentication functions, as well as file and folder permissions. The advantage of an application-based user authentication system is primarily in cost. To use Windows authentication, we need to purchase Client Access Licenses (CALs) for each user that will access our application. This is practical in an intranet, where users would have these licenses to perform other functions in the network. However, for an Internet application, with potentially thousands of users, licensing could be extremely expensive. The drawback to an application-based system is that there is a lot more work to do in designing and using it. The Windows authentication process has been around for years, continually improved by Microsoft with each Windows release. It scales extremely well, and with Active Directory, can be extended to manage just about anything you can think of. ASP.NET membership Fortunately, Microsoft has provided relief for application-based authentication drawbacks in the 2.0 version of the ASP.NET framework, with the ASP.NET membership functions, and in our case, the SqlMembershipProvider. The membership API makes it simple for us to use forms authentication in our application, retrieving authentication and membership information from a membership provider. The membership provider abstracts the membership details from the membership storage source. Microsoft provides two providers—the ActiveDirectoryMembershipProvider that uses Active Directory and the SqlMembershipProvider that uses an SQL server database for the user data store. By default, ASP.NET authentication uses cookies—small text files stored on the user's system—to maintain authentication status throughout the application. These cookies normally have an expiration time and date, which requires users to log in again after the cookie has expired. It is possible to use cookies to allow the client system to authenticate the application without a user login, commonly seen as a "Remember Me" checkbox in many web site login pages. There is naturally a downside to cookies in that a client system may not accept cookies. ASP.NET can encode the authentication information into the URL to bypass this restriction on cookies. Although in the case of our application, we will stick with the cookie method. Forms authentication secures only ASP.NET pages. Unless you are using IIS7, and the integrated pipeline, where ASP.NET processes all file requests, the ASP.NET DLL won't be called for non-ASP.NET pages. This means that you cannot easily secure HTML pages, PDF files, or anything other than ASP.NET through forms authentication. Configuring and using forms authentication Let's start learning ASP.NET forms authentication by walking through a brand new application. We'll then add it to our Content Management System application. Forms authentication is actually quite simple, both in concept and execution, and a simple application can explain it better than adopting our current CMS application. Of course, we eventually need to integrate authentication into our CMS application, but this is also easier once you understand the principles and techniques we'll be using. Creating a new application Start by opening Visual Web Developer 2008 Express and creating a new web site by clicking on File | New Web Site. Use the ASP.NET Website template, choose File System, and name the folder FormsDemo. When the site is created, you are presented with a Default.aspx page created with generic code. We will use this as our home page for the new site, although we need to modify it for our needs. Creating the home page Visual Web Developer 2008 Express creates a generic Default.aspx file whenever you create a new site. Unfortunately, the generic file is not what we want and will need modification. The first thing we want to do is make sure our site uses a Master Page, just as our Content Management System application will. To do this, we could delete the page, create our Master Page, and then add a new Default.aspx page that uses our Master Page. In the case of a brand new site, it's pretty easy, but what if you have developed an extensive site that you want to convert to Master Pages? You would want to add a Master Page to an existing site, so let's go ahead and do that. Create the Master Page To create a Master Page, leave the Default.aspx file open and press Ctrl+Shift+A to add a new item to the solution. Choose the Master Page template and leave the name as MasterPage.Master. Place the code in a separate file and click Add to create the Master Page. You will notice that this creates the same generic code as in the previous chapter. Unfortunately, our Default.aspx file is not a content page and won't use the MasterPage.Master we just created, unless we tell it to. To tell our Default.aspx page to use the MasterPage.Master, we need to add the MasterPageFile declaration, in the @ Page declaration, at the top of the file. Add the following code between the Language and AutoEventWireup declarations: MasterPageFile="~/MasterPage.master" This adds the Master Page to our Default.aspx page. However, content pages include only those Content controls that match the Master Page, not the full page code as our Default.aspx page currently does. To fix this, replace the remaining code outside the @ Page declaration with the following two Content controls: <asp:Content ID="Content1" ContentPlaceHolderID="head" Runat="Server"></asp:Content><asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server"> <h1>This is where the content goes.</h1></asp:Content> We've left the Content1 control empty for the moment, and we've added a simple text statement to the Content2 control so that it can be tested. If you view the Default.aspx page in a browser, you should see the relatively uninteresting web page below: Enabling forms authentication Okay, we have a boring home page for our new site. Let's leave it for a moment and enable forms authentication for the site, so we can restrict who can access our home page. The process of enabling forms authentication is simply adding a few lines to our web.config file. Or in the case of the generic web.config file, which we created while creating our new site, we simply need to alter a single line. Open the web.config file in the new site and look for the line that says: <authentication mode="Windows" /> Edit it to read: <authentication mode="Forms" /> Save the web.config file and you have now enabled forms authentication for this site. The default authentication mode for ASP.NET applications is Windows, which is fine if you're working in an intranet environment where every user probably has a Windows login for use in the corporate network anyway. Using Windows authentication, Windows itself handles all the security and authentication, and you can use the myriad of Windows utilities and functions such as Active Directory, to manage your users. On the other hand, with forms authentication, ASP.NET is expected to handle all the details of authentication and security. While ASP.NET 2.0 and later have sophisticated membership and profile capabilities, there is no ASP.NET mechanism for protecting files and folders from direct access, outside of the application. You will still need to secure the physical server and operating system from outside of your application. Creating the membership database To use forms authentication and the SqlMembershipProvider, we need to create a database to authenticate against. This database will hold our user information, as well as membership information, so we can both authenticate the user and provide access based on membership in specific roles. For our demonstration, we will create a new database for this function. We'll create a database with SQL Server ManagementExpress, so open it and right-click Databases in the Object Explorer pane. Choose New Database and name it FormsDemo. Change the location of the database path to the App_Data folder of your FormsDemo web application—the default is C:InetpubFormsDemoApp_Data as shown below. Click OK and the new database will be created. If you look at this database, you will see that it is empty. We haven't added any tables to it, and we haven't set up any fields in those non-existent tables. The database is pretty much useless at this stage. We need to create the database layout, or schema, to hold all the authentication and membership details. Fortunately, Microsoft provides a simple utility to accomplish this task for the 2.0 version of the ASP.NET framework – aspnet_regsql.exe. We'll use this too, in order to create the schema for us, and make our database ready for authentication and membership in our application. To use aspnet_regsql.exe, we need to provide the SQL Server name and login information. This is the same information as shown in the login dialog when we open the database in SQL Server Management Studio Express, as shown below: Note the server name, it will usually be {SystemName}/SQLEXPRESS, but it may be different depending on how you set it up. We use SQL Server Authentication with the sa account and a password of SimpleCMS when we set up SQL Server Express 2005, and that's what we'll use when we run the aspnet_regsql.exe tool. To run aspnet_regsql.exe, you may browse to it in Windows Explorer, or enter the path into the Run dialog when you click on Start and then Run. The default path is C:WINDOWSMicrosoft.NETFrameworkv2.0.50727aspnet_regsql.exe. The utility may be run with command-line arguments, useful when scripting the tool or using it in a batch file, but simply running it with no parameters brings it up in a GUI mode. When the ASP.NET SQL Server Setup Wizard launches, click Next. Make sure that the Configure SQL Server for application services is selected and click on Next. The ASP.NET SQL Server Setup Wizard will ask for the server, authentication, and database. You should enter these according to the information from above. Click Next to confirm the settings. Click Next again to configure the database with the ASP.NET users and membership schema. Continue and exit the wizard, and the database is ready for us to use for authentication. If you were to open the FormsDemo database in SQL Server Management Studio Express, you would find that new tables, views, and stored procedures have been added to the database during this configuration process.
Read more
  • 0
  • 0
  • 2018

article-image-creating-shopping-cart-using-zend-framework-part-2
Packt
27 Oct 2009
15 min read
Save for later

Creating a Shopping Cart using Zend Framework: Part 2

Packt
27 Oct 2009
15 min read
Creating the Cart Views and Forms Now that we have our Model and Controller created, we can now start putting everything together and get the cart working. Cart forms The Cart will use two forms Storefront_Form_Cart_Add and Storefront_Form_Cart_Table. The add form is displayed next to the products so users can add items to the Cart, and the table form is used to display all the items in the cart so users can edit them. Add form The add form can be used by customers browsing the store to quickly add items to their shopping cart. This form will look like the one shown in the screenshot below when it is rendered: Let's add the code to create the add form now. application/modules/storefront/forms/Cart/Add.php class Storefront_Form_Cart_Add extends SF_Form_Abstract { public function init() { $this->setDisableLoadDefaultDecorators(true); $this->setMethod('post'); $this->setAction(''); $this->setDecorators(array( 'FormElements', 'Form' )); $this->addElement('text', 'qty', array( 'decorators' => array( 'ViewHelper' ), 'style' => 'width: 20px;', 'value' => 1 )); $this->addElement('submit', 'buy-item', array( 'decorators' => array( 'ViewHelper' ), 'label' => 'Add to cart' )); $this->addElement('hidden', 'productId', array( 'decorators' => array( 'ViewHelper' ), )); $this->addElement('hidden', 'returnto', array( 'decorators' => array( 'ViewHelper' ), )); } } The add form contains four elements—qty, buy-item, productId, and returnto. We can see that it is much like the other forms we have created previously. The only major difference here is that we use the setDisableLoadDefaultDecorators() method to disable the default decorators for the form (not the elements). We do this because we do not want the form to contain the default definition list markup (<dl>). We also only use the ViewHelper decorator on each element so that the <dt> and <dd> tags are omitted Table form The table form is going to form the customer shopping cart. Customers will use this form to view, update, and remove items from their cart. This form will look similar to the one showed below when it is rendered: Let's add the code for the table form now: application/modules/storefront/forms/Cart/Table.php class Storefront_Form_Cart_Table extends SF_Form_Abstract { public function init() { $this->setDisableLoadDefaultDecorators(true); $this->setDecorators(array( array( 'ViewScript', array('viewScript' => 'cart/_cart.phtml') ), 'Form' )); $this->setMethod('post'); $this->setAction(''); $this->addElement('submit', 'update-cart', array( 'decorators' => array( 'ViewHelper' ), 'label' => 'Update' )); } } Th e table form is highly specialized. Therefore, we have chosen to use a ViewScript decorator. To do this, we fi rst disable the default decorators using the setDisableLoadDefaultDecorators(). We then need to configure the forms decorators. We will only have two decorators for the form, ViewScript and Form. This means that if we render the form, the update-cart element will not be rendered because we have not included the FormElements decorator. This is where the ViewScript decorator comes in. We can use this decorator to render a View script, in this case cart/_cart.phtml. We then have access to all the elements within the form inside this View script, meaning we can create highly specialized markup without needing to use lots of complicated decorators. Also, the table form will need to have fi elds dynamically added to it as we need a form element for each cart item. We will look at this shortly when we create the View Helper and Views for the Cart. The ViewScript decorator uses a View Partial to render its view script. This has an overhead as it clones the view instance. Generally, partials should be avoided in large numbers so do not over use them or the ViewScript decorator. SF_Form_Abstract You may have noticed that our forms did not subclass Zend_Form as in our previous examples. Also, this time we have extended from the SF_Form_Abstract class. This is because we have done some minor refactoring to the SF library so that we can inject the Model into the form. library/SF/Form/Abstract.php class SF_Form_Abstract extends Zend_Form { protected $_model; public function setModel(SF_Model_Interface $model) { $this->_model = $model; } public function getModel() { return $this->_model; } } The new SF_Form_Abstract class subclasses Zend_Form and adds two new methods, setModel() and getModel(). These simply set, and get, the protected $_model property. This then means that when we instantiate the form, we can pass in the model inside the options array. $form = new SF_Form_Abstract(array('model' => new myModel())); Here we are taking advantage of the fact that the setOptions() method will look for setters that match elements in the options array. In our case, the setOptions() class will find the setModel() method, call it, and pass in the model. This type of functionality is very common in Zend Framework components. It is always worth checking the setOptions() methods on components to see if you can extend them in this way. To get the model injected on instantiation, we also need to make a minor change to the SF_Model_Abstract. library/SF/Model/Abstract.php public function getForm($name) { if (!isset($this->_forms[$name])) { $class = join('_', array( $this->_getNamespace(), 'Form', $this->_getInflected($name) )); $this->_forms[$name] = new $class( array('model' => $this) ); } return $this->_forms[$name]; } He re, we simply pass in an array containing the model ($this) when we first instantiate the form class. We now have access to our Model from within our forms. Cart View Helper Th e Cart View Helper is responsible for creating many of the display elements for the cart. Therefore, we will break it down and look at each method in turn. application/modules/storefront/views/helpers/Cart.php class Zend_View_Helper_Cart extends Zend_View_Helper_Abstract { public $cartModel; public function Cart() { $this->cartModel = new Storefront_Model_Cart(); return $this; } The main Cart() method instantiates a new Cart Model and then returns a reference to itself so that we can chain calls to the other methods. application/modules/storefront/views/helpers/Cart.php public function getSummary() { $currency = new Zend_Currency(); $itemCount = count($this->cartModel); if (0 == $itemCount) { return '<p>No Items</p>'; } $html = '<p>Items: ' . $itemCount; $html .= ' | Total: '.$currency->toCurrency ($this->cartModel->getSubTotal()); $html .= '<br /><a href="'; $html .= $this->view->url(array( 'controller' => 'cart', 'action' => 'view', 'module' => 'storefront' ), 'default', true ); $html .= '">View Cart</a></p>'; return $html; } The getSummary() method creates the HTML that will be used to display a summary of the cart items and subtotal to the user. This will be displayed below the main category menus. application/modules/storefront/views/helpers/Cart.php public function addForm(Storefront_Resource_Product_Item$product) { $form = $this->cartModel->getForm('cartAdd'); $form->populate(array( 'productId' => $product->productId, 'returnto' => $this->view->url() )); $form->setAction($this->view->url(array( 'controller' => 'cart', 'action' => 'add', 'module' => 'storefront' ), 'default', true )); return $form; } The addForm() method will return a form for adding a single product to the cart. This method accepts one parameter $product that must be an instance of Storefront_Resource_Product_Item. We will use this to render individual add to cart forms for each product. application/modules/storefront/views/helpers/Cart.php public function cartTable() { $cartTable = $this->cartModel->getForm('cartTable'); $cartTable->setAction($this->view->url(array( 'controller' => 'cart' , 'action' => 'update' ), 'default' )); $qtys = new Zend_Form_SubForm(); foreach($this->cartModel as $item) { $qtys->addElement('text', (string) $item->productId, array( 'value' => $item->qty, 'belongsTo' => 'quantity', 'style' => 'width: 20px;', 'decorators' => array( 'ViewHelper' ), ) ); } $cartTable->addSubForm($qtys, 'qtys'); // add shipping options $cartTable->addElement('select', 'shipping', array( 'decorators' => array( 'ViewHelper' ), 'MultiOptions' => $this->_getShippingMultiOptions(), 'onChange' => 'this.form.submit();', 'value' => $this->cartModel->getShippingCost() )); return $cartTable; } The cartTable() method will return the table containing all our cart items, their costs, and totals. This will be used to update items in the cart. We create a subform to dynamically add the cart items quantity elements at runtime. The reason we use a subform is so we can easily get the whole set of quantity fi elds from the form, and later iterate over them in the View script. The form will need to contain an array of quantity text elements so that we can iterate over them in the updateAction in the controller. To create this array, we pass the belongsTo option to the addElement() method, which will tell the form that these elements are an array with the name quantity. We also set the value of the element to the qty held in the cart item. We also need a way of passing the productId for each cart item. To do this, we set the element name to the productId of the item. This also helps us by providing a unique name for each element (we have to cast this to a string). It will create a set of text form elements like: <input type="text" style="width: 20px;" value="1" id="quantity-21"name="quantity[21]"/><input type="text" style="width: 20px;" value="5" id="quantity-10"name="quantity[10]"/> Once we have all the quantity elements in the subform, we then add the whole subform to the main table form using the addSubForm() method. We give this the name of qtys, which we will use in the View script later to retrieve the elements. We also add the shipping options to the main table form. Here, we use the _getShippingMultiOptions() method to populate the select elements options and set the value to the currently selected shipping option of the cart. application/modules/storefront/views/helpers/Cart.php public function formatAmount($amount) { $currency = new Zend_Currency(); return $currency->toCurrency($amount); } The formatAmount() method is a little helper method we use to display amounts from the Cart. This may not be necessary in the future as there is a proposal for a currency View Helper that we would use instead. application/modules/storefront/views/helpers/Cart.php private function _getShippingMultiOptions() { $currency = new Zend_Currency(); $shipping = new Storefront_Model_Shipping(); $options = array(0 => 'Please Select'); foreach($shipping->getShippingOptions() as $key => $value) { $options["$value"] = $key . ' - ' . $currency->toCurrency($value); } return $options; } } Our final method is the private _getShippingMultiOptions() method. This is used internally by the cartTable() method to populate the shipping select element's options. This method gets the shipping options from the Shipping Model and creates an array suitable for the multiOptions option. Cart View scripts Now that we have all the tools created that we will need to build our cart, we can start creating the user interface. Cart view.phtml The view.phtml is the View that is rendered by the viewAction of the CartController. This View includes a title and renders the cartTable form application/modules/storefront/views/scripts/cart/view.phtml <h3>shopping <span>cart</span></h3> <?=$this->Cart()->cartTable();?> Cart _cart.phtml The ViewScript decorator attached to the table form will render the _cart.phtml View. When it renders, the ViewScript decorator will create a view partial and pass in the form as the element property for this View script. application/modules/storefront/views/scripts/cart/_cart.phtml <div style="padding: 8px;"> <table style="width: 100%;"> <tbody> <? $i = 0; foreach($this->element->getModel() as $item): ?> <tr <? if($i % 2){ echo 'class="odd"';};?>> <td><?=$this->Escape($item->name); ?></td> <td><?=$this->element->qtys->getElement ($item->productId); ?></td> <td class="rt"><?=$this->Cart()->formatAmount ($item->getLineCost()); ?></td> </tr> <? ++$i; endforeach; ?> <tr> <td colspan="2" class="rt">SubTotal:</td> <td class="rt colRight"><?=$this->Cart() ->formatAmount($this->element->getModel() ->getSubTotal()); ?></td> </tr> <tr> <td colspan="2" class="rt">Shipping: <?=$this->element ->getElement('shipping');?></td> <td class="rt colRight"><?=$this->Cart() ->formatAmount($this->element->getModel() ->getShippingCost()); ?></td> </tr> <tr> <td colspan="2" class="rt">Total:</td> <td class="rt"><?=$this->Cart()->formatAmount($this ->element->getModel()->getTotal()); ?></td> </tr> </tbody></table><?=$this->element->getElement('update-cart'); ?></div> The HTML produced by this script will look similar to the following screenshot: The main aspect here is the line items. We need to iterate over the cart and display each product line item. <?$i = 0;foreach($this->element->getModel() as $item):?> <tr <? if($i % 2){ echo 'class="odd"';};?>> <td><?=$this->Escape($item->name); ?></td> <td><?=$this->element->qtys->getElement($item->productId); ?> </td> <td class="rt"><?=$this->Cart()->formatAmount($item->getLineCost()); ?> </td> </tr><?++$i;endforeach;?> Here, we get the Cart Model from the form using our new getModel() method that we created earlier in the SF_Form_Abstract and iterate over it. As we iterate over the Cart Model, we display all the products and line costs. We also get the quantity form elements. To retrieve the correct quantity form element for each product, we access the qtys subform and use the getElement() method. We pass in the items productId as we named our quantity form elements using the productId earlier. All of the other form data is rendered in a similar way. We either get data from the Cart Model, or get elements from the form itself. By using the ViewScript decorator, we can see that it is much easier to mix form and non-form elements. Layout main.phtml application/layouts/scripts/main.phtml <div class="left categorylist"> <?= $this->layout()->categoryMain; ?> <? if (0 < count($this->subCategories)):?> <div class="sub-nav"> <h3>in this <span>category</span></h3> <ul> <? foreach ($this->subCategories as $category): ?> <li><a href="<?=$this->url(array('categoryIdent' => $category->ident), 'catalog_category', true );?>"><?=$category->name; ?></a></li> <? endforeach; ?> </ul> </div> <? endif; ?> <div> <h3>in your <span>cart</span></h3> <?= $this->Cart()->getSummary(); ?> </div> </div> We need to display the cart summary to the users so that they can see a brief overview of the items in their cart. To do this, we will use the Cart View Helper and the getSummary() method that looks similar to the following screenshot: Catalog index.phtml application/modules/storefront/view/scripts/catalog/index.phtml <p><?=$this->productPrice($product); ?></p> <?=$this->Cart()->addForm($product); ?> When displaying a list of products, we want the user to be able to add the product to their cart at that point. To do this, we render the cart add form under the price. This will make our catalog listing look like the one shown below: Catalog view.phtml application/modules/storefront/view/scripts/catalog/view.phtml <p><?=$this->productPrice($this->product); ?></p> <?=$this->Cart()->addForm($this->product); ?> Just like the index.phtml, we need to render the cart add form after the product price. This will make our details page look like this: Summary In this two-part article series, we learnt about: Creating Models that do not use a database as a data source Using Zend_Session_Namespace Implementing the Cart Views and Controllers More Forms, View Helpers, and so on If you have read this article you may be interested to view : Creating a Shopping Cart using Zend Framework: Part 1
Read more
  • 0
  • 0
  • 2242
Visually different images

article-image-creating-shopping-cart-using-zend-framework-part-1
Packt
27 Oct 2009
13 min read
Save for later

Creating a Shopping Cart using Zend Framework: Part 1

Packt
27 Oct 2009
13 min read
Our next task in creating the storefront is to create the shopping cart. This will allow users to select the products they wish to purchase. Users will be able to select, edit, and delete items from their shopping cart. Lets get started. Creating the Cart Model and Resources We will start by creating our model and model resources. The Cart Model differs from our previous model in the fact that it will use the session to store its data instead of the database. Cart Model The Cart Model will store the products that they wish to purchase. Therefore, the Cart Model will contain Cart Items that will be stored in the session. Let's create this class now. application/modules/storefront/models/Cart.php class Storefront_Model_Cart extends SF_Model_Abstract implements SeekableIterator, Countable, ArrayAccess { protected $_items = array(); protected $_subTotal = 0; protected $_total = 0; protected $_shipping = 0; protected $_sessionNamespace; public function init() { $this->loadSession(); } public function addItem( Storefront_Resource_Product_Item_Interface $product,$qty) { if (0 > $qty) { return false; } if (0 == $qty) { $this->removeItem($product); return false; } $item = new Storefront_Resource_Cart_Item( $product, $qty ); $this->_items[$item->productId] = $item; $this->persist(); return $item; } public function removeItem($product) { if (is_int($product)) { unset($this->_items[$product]); } if ($product instanceof Storefront_Resource_Product_Item_Interface) { unset($this->_items[$product->productId]); } $this->persist(); } public function setSessionNs(Zend_Session_Namespace $ns) { $this->_sessionNamespace = $ns; } public function getSessionNs() { if (null === $this->_sessionNamespace) { $this->setSessionNs(new Zend_Session_Namespace(__CLASS__)); } return $this->_sessionNamespace; } public function persist() { $this->getSessionNs()->items = $this->_items; $this->getSessionNs()->shipping = $this->getShippingCost(); } public function loadSession() { if (isset($this->getSessionNs()->items)) { $this->_items = $this->getSessionNs()->items; } if (isset($this->getSessionNs()->shipping)) { $this->setShippingCost($this->getSessionNs()->shipping); } } public function CalculateTotals() { $sub = 0; foreach ($this as $item) { $sub = $sub + $item->getLineCost(); } $this->_subTotal = $sub; $this->_total = $this->_subTotal + (float) $this->_shipping; } public function setShippingCost($cost) { $this->_shipping = $cost; $this->CalculateTotals(); $this->persist(); } public function getShippingCost() { $this->CalculateTotals(); return $this->_shipping; } public function getSubTotal() { $this->CalculateTotals(); return $this->_subTotal; } public function getTotal() { $this->CalculateTotals(); return $this->_total; } /*...*/ } We can see that the Cart Model class is fairly weighty and in fact, we have not included the full class here. The reason we have slightly truncated the class is that we are implementing the SeekableIterator, Countable, and ArrayAccess interfaces. These interfaces are defined by PHP's SPL Library and we use them to provide a better way to interact with the cart data. For the complete code, copy the methods below getTotal() from the example files for this article. We will look at what each method does shortly in the Cart Model implementation section, but first, let's look at what functionality the SPL interfaces allow us to add. Cart Model interfaces The SeekableIterator interface allows us to access our cart data in these ways: // iterate over the cartforeach($cart as $item) {}// seek an item at a position$cart->seek(1);// standard iterator access$cart->rewind();$cart->next();$cart->current(); The Countable interface allows us to count the items in our cart: count($cart); The ArrayAccess interface allows us to access our cart like an array: $cart[0]; Obviously, the interfaces provide no concrete implementation for the functionality, so we have to provide it on our own. The methods not listed in the previous code listing are: offsetExists($key) offsetGet($key) offsetSet($key, $value) offsetUnset($key) current() key() next() rewind() valid() seek($index) count() We will not cover the actual implementation of these interfaces, as they are standard to PHP. However, you will need to copy all these methods from the example files to get the Cart Model working. Documentation for the SPL library can be found athttp://www.php.net/~helly/php/ext/spl/     Cart Model implementation Going back to our code listing, let's now look at how the Cart Model is implemented. Let's start by looking at the properties and methods of the class. The Cart Model has the following class properties: $_items:An array of cart items $_subTotal: Monetary total of cart items $_total: Monetary total of cart items plus shipping $_shipping: The shipping cost $_sessionNamespace: The session store The Cart Model has the following methods: init(): Called during construct and loads the session data addItem(Storefront_Resource_Product_Item_Interface $product, $qty): Adds or updates items in the cart removeItem($product): Removes a cart item setSessionNs(Zend_Session_Namespace $ns): Sets the session instance to use for storage getSessionNs(): Gets the current session instance persist(): Saves the cart data to the session loadSession(): Loads the stored session values calculateTotals(): Calculates the cart totals setShippingCost($cost): Sets the shipping cost getShippingCost(): Gets the shipping cost getSubTotal(): Gets the total cost for items in the cart (not including the shipping) getTotal(): Gets the subtotal plus the shipping cost When we instantiate a new Cart Model instance, the init() method is called. This is defined in the SF_Model_Abstract class and is called by the __construct() method. This enables us to easily extend the class's instantiation process without having to override the constructor. The init() method simply calls the loadSession() method. This method populates the model with the cart items and shipping information stored in the session. The Cart Model uses Zend_Session_Namespace to store this data, which provides an easy-to-use interface to the $_SESSION variable. If we look at the loadSession() method, we see that it tests whether the items and shipping properties are set in the session namespace. If they are, then we set these values on the Cart Model. To get the session namespace, we use the getSessionNs() method. This method checks if the $_sessionNs property is set and returns it. Otherwise it will lazy load a new Zend_Session_Namespace instance for us. When using Zend_Session_ Namespace, we must provide a string to its constructor that defines the name of the namespace to store our data in. This will then create a clean place to add variables to, without worrying about variable name clashes. For the Cart Model, the default namespace will be Storefront_Model_Cart. The Zend_Session_Namespace component provides a range of functionality that we can use to control the session. For example, we can set the expiration time as follows: $ns = new Zend_Session_Namespace('test');$ns->setExpirationSeconds(60, 'items');$ns->setExpirationHops(10);$ns->setExpirationSeconds(120); This code would set the item's property expiration to 60 seconds and the namespaces expiration to 10 hops (requests) or 120 seconds, whichever is reached first. The useful thing about this is that the expiration is not global. Therefore, we can have specialized expiration per session namespace. There is a full list of Zend_Session_Namespace functionalities in the reference manual. Testing with Zend_Session and Zend_Session_NamespaceTesting with the session components can be fairly diffi cult. For the Cart Model, we use the setSessionNs() method to allow us to inject a mock object for testing, which you can see in the Cart Model unit tests. There are plans to rewrite the session components to make testing easier in the future, so keep an eye out for those updates. To add an item to the cart, we use the addItem() method. This method accepts two parameters,$product and $qty. The $product parameter must be an instance of the Storefront_Resource_Product_Item class, and the $qty parameter must be an integer that defines the quantity that the customer wants to order. If the addItem() method receives a valid $qty, then it will create a new Storefront_Resource_Cart_Item and add it to the $_items array using the productId as the array key. We then call the persist() method. This method simply stores all the relevant cart data in the session namespace for us. You will notice that we are not using a Model Resource in the Cart Model and instead we are directly instantiating a Model Resource Item. This is because the Model Resources represent store items and the Cart Model is already doing this for us so it is not needed. To remove an item, we use the removeItem() method. This accepts a single parameter $product which can be either an integer or a Storefront_Resource_Product_Item instance. The matching cart item will be removed from the $_items array and the data will be saved to the session. Also, addItem() will call removeItem() if the quantity is set to zero. The other methods in the Cart Model are used to calculate the monetary totals for the cart and to set the shipping. We will not cover these in detail here as they are fairly simple mathematical calculations. Cart Model Resources Now that we have our Model created, let's create the Resource Interface and concrete Resource class for our Model to use. application/modules/storefront/models/resources/Cart/Item/Interface.php interface Storefront_Resource_Cart_Item_Interface { public function getLineCost(); } The Cart Resource Item has a very simple interface that has one method, getLineCost(). This method is used when calculating the cart totals in the Cart Model. application/modules/storefront/models/resources/Cart/Item.php class Storefront_Resource_Cart_Item implements Storefront_Resource_Cart_Item_Interface { public $productId; public $name; public $price; public $taxable; public $discountPercent; public $qty; public function __construct(Storefront_Resource_Product_Item_ Interface $product, $qty) { $this->productId = (int) $product->productId; $this->name = $product->name; $this->price = (float) $product->getPrice(false,false); $this->taxable = $product->taxable; $this->discountPercent = (int) $product->discountPercent; $this->qty = (int) $qty; } public function getLineCost() { $price = $this->price; if (0 !== $this->discountPercent) { $discounted = ($price*$this->discountPercent)/100; $price = round($price - $discounted, 2); } if ('Yes' === $this->taxable) { $taxService = new Storefront_Service_Taxation(); $price = $taxService->addTax($price); } return $price * $this->qty; } } The concrete Cart Resource Item has two methods __construct() and getLineCost(). The constructor accepts two parameters $product and $qty that must be a Storefront_Resource_Product_Item_Interface instance and integer respectively. This method will then simply copy the values from the product instance and store them in the matching public properties. We do this because we do not want to simply store the product instance because it has all the database connection data contained within. This object will be serialized and stored in the session. The getLineCost() method simply calculates the cost of the product adding tax and discounts and then multiplies it by the given quantity. Shipping Model We also need to create a Shipping Model so that the user can select what type of shipping they would like. This Model will simply act as a data store for some predefined shipping values. application/modules/storefront/models/Shipping.php class Storefront_Model_Shipping extends SF_Model_Abstract { protected $_shippingData = array( 'Standard' => 1.99, 'Special' => 5.99, ); public function getShippingOptions() { return $this->_shippingData; } } The shipping Model is very simple and only contains the shipping options and a single method to retrieve them. In a normal application, shipping would usually be stored in the database and most likely have its own set of business rules. For the Storefront, we are not creating a complete ordering process so we do not need these complications. Creating the Cart Controller With our Model and Model Resources created, we can now start wiring the application layer together. The Cart will have a single Controller, CartController that will be used to add, view, and update cart items stored in the Cart Model. application/modules/storefront/controllers/CartController.php class Storefront_CartController extends Zend_Controller_Action { protected $_cartModel; protected $_catalogModel; public function init() { $this->_cartModel = new Storefront_Model_Cart(); $this->_catalogModel = new Storefront_Model_Catalog(); } public function addAction() { $product = $this->_catalogModel->getProductById( $this->_getParam('productId') ); if(null === $product) { throw new SF_Exception('Product could not be added to cart as it does not exist' ); } $this->_cartModel->addItem( $product, $this->_getParam('qty') ); $return = rtrim( $this->getRequest()->getBaseUrl(), '/' ) . $this->_getParam('returnto'); $redirector = $this->getHelper('redirector'); return $redirector->gotoUrl($return); } public function viewAction() { $this->view->cartModel = $this->_cartModel; } public function updateAction() { foreach($this->_getParam('quantity') as $id => $value) { $product = $this->_catalogModel->getProductById($id); if (null !== $product) { $this->_cartModel->addItem($product, $value); } } $this->_cartModel->setShippingCost( $this->_getParam('shipping') ); return $this->_helper->redirector('view'); } } The Cart Controller has three actions that provide a way to: add: add cart items view: view the cart contents update: update cart items The addAction() first tries to find the product to be added to the cart. This is done by searching for the product by its productId field, which is passed either in the URL or by post using the Catalog Model. If the product is not found, then we throw an SF_Exception stating so. Next, we add the product to the cart using the addItem() method. When adding the product, we also pass in the qty. The qty can again be either in the URL or post. Once the product has been successfully added to the cart, we then need to redirect back to the page where the product was added. As we can have multiple locations, we send a returnto variable with the add request. This will contain the URL to redirect back to, once the item has been added to the cart. To stop people from being able to redirect away from the storefront, we prepend the baseurl to the redirect string. To perform the actual redirect, we use the redirector Action Helper's gotoUrl() method. This will create an HTTP redirect for us. The viewAction() simply assigns the Cart Model to the cartModel View property. Most of the cart viewing functionality has been pushed to the Cart View Helper and Forms, which we will create shortly. The updateAction() is used to update the Cart Items already stored in the cart. The first part of this updates the quantities. The quantities will be posted to the Action as an array in the quantity parameter. The array will contain the productId as the array key, and the quantity as the value. Therefore, we iterate over the array fi nding the product by its ID and adding it to the cart. The addItem() method will then update the quantity for us if the item exists and remove any with a zero quantity. Once we have updated the cart quantities, we set the shipping and redirect back to the viewAction. >> Continue Reading Creating a Shopping Cart using Zend Framework: Part 2
Read more
  • 0
  • 0
  • 3775

article-image-creating-student-blog-drupal-using-cloning
Packt
27 Oct 2009
6 min read
Save for later

Creating the Student Blog in Drupal using Cloning

Packt
27 Oct 2009
6 min read
For the purpose of this article we will clone the already existing Teacher Blog and create the Student Blog. Setting Up the Student Blog To create the student blog, we need to do two things: Give users in the student role permissions over the blog post content type. Clone the teacher_blog view, and edit it to display student blog posts. Assigning Permissions To allow students to blog in the site, we need to allow users in the student role the ability to create blog posts. Click the Administer | User management | Roles link, or navigate to admin/user/roles. Click the link to edit permissions for the student role. Navigate down to the section for the node module. Select the options for create blog_post content, delete own blog_post content, and edit own blog_post content. Click the Save permissions button to save the settings. Students can now blog in the site. Clone the Teacher Blog Now that students have the ability to create blog posts, we now need to create a central place where people can read these posts. We have already set up this structure for the teacher blog; cloning this pre-existing view will allow us to quickly replicate this structure for the student blog. To begin, click the Administer | Site building | Views link, or navigate to admin/build/views. Scroll down to the teacher_blog view and click the Clone link. Change the view name to student_blog; change the view description to All posts to be displayed in the Student blog; change the View tag to student. Click the Next button to continue. In the default settings, we want to change the User: Roles filter. As shown in the following screenshot, you can verify that you are editing the Defaults as indicated by Item 1; to edit the User: Roles filter, click the link as indicated by Item 2; and to edit the Title, click the link indicated by Item 3. Change the User: Roles setting to student; this will only select content posted by users in the student role. Change the Title setting to Student blog. As we add more content types (audio, video, and images) we will need to revisit this view to update the Node:Type filter. At this stage, this filter only selects blog posts and bookmarks. Then, as shown in the following screenshot, click the Page link (indicated by Item 1) to change the settings for the Page display for this view. We need to edit both of the options under Page settings (indicated by Item 2). We also need to edit the Header (indicated by Item 3) in the Basic settings. Under Page settings, change the Path to student-blog, and change Menu to Normal: Student blog. Under Basic settings, edit the Header to read Hello! You are viewing posts from the student blog. Enjoy your reading, and comment frequently. Click the Save button to save the view. All student blog posts are now visible at http://yoursite.org/student-blog. Getting Interactive Now that students can create blogs in the site, you have the ability to foster dialogue within your class. The easiest way, of course, is simply through commenting. Students have the rights to comment on assignments, and on teacher and student blog posts. However, students might also want to reference other pieces of content in their work. In this section, we will set up a mechanism that will keep track of when one post within the site references another post within the site. This way, people can see when exchanges are occurring about different posts, and it provides another way (in addition to comment threads) for people to hold discussions within the course. Seeing Who's Discussing What Within the site, we will want to see who is discussing what posts. In web parlance, this is referred to as a backlink. Fortunately, the Views module comes with a means of tracking backlinks by default. We will clone and customize this existing view to get exactly the functionality we want. The process of cloning this view includes the following steps: The default backlinks view needs to be enabled and cloned. In the cloned view, the different displays need to be edited: In the Default display, Fields need to be added to the view, the Arguments need to be adjusted, and the Empty text needs to be deleted. As the new view will only generate a block, the Page display should be removed. In the Block display, the Items per page needs to be increased, the More link needs to be removed, and the Block settings needs to be changed. Then, once the new view has been saved, the block created by this view needs to be enabled. Enabling and Cloning the Backlinks View To get started, click the Administer | Site building | Views link, or navigate to admin/build/views. As shown in the following screenshot, enable the default backlinks view. Once we have enabled the backlinks view, we want to clone it. So, we click the Clone link. Change the View name to conversations, and change the View description to Cloned from default "backlinks" view; displays a list of nodes that link to the node, using the search backlinks table. The View tag can be left blank. Click the Next button, which brings us to the Edit page for the view. Editing the Default Display As shown in the following screenshot, we will make four main edits to this view. We will add Fields, adjust the Arguments, delete the Empty text, and remove the Page display. To add Fields, click the + icon as indicated, in the preceding screenshot, by Item 1. Add three fields: Node: Post Date; Node: Type; and User: Name. Click the Add button, and then configure the new fields to your preferences. Next, edit the Arguments by clicking the Search: Links to link as indicated in the preceding screenshot by Item 2. We will edit the argument handling as shown in the following screenshot: Select the options to only validate for Blog posts and Bookmarks. Additionally, check the option for Validate user has access to the node. These argument settings confirm that we are only checking for backlinks on Blog posts and Bookmarks. As we add more content types (for audio, video, and images) we will need to update this view to check for backlinks on these additional content types as well. Click the Update button to store these changes. Then, we will remove the Empty text by clicking the Filtered HTML link as indicated by Item 3 in the screenshot just above the preceding one. Delete the existing empty text string, and click the Update button to store the changes. Deleting the empty text makes it so the view will not be displayed if the view returns no content. Although this would not be useful on a Page display, it is useful for a Block display, as this hides the block when there is nothing to show.
Read more
  • 0
  • 0
  • 1064

article-image-coldfusion-ajax-programming
Packt
27 Oct 2009
9 min read
Save for later

ColdFusion AJAX Programming

Packt
27 Oct 2009
9 min read
Binding When it comes to programming, the two most commonly used features are CFAJAXProxy and binding. The binding feature allows us to bind or tie things together by using a simpler technique than we would otherwise have needed to create. Binding acts as a double-ended connector in some scenarios. You can set the bind to pull data from another ColdFusion tag on the form. These must be AJAX tags with binding abilities. There are four forms of bindings, on page, CFC, JavaScript, and URL. Let's work through each style so that we will understand them well. We will start with on page binding. Remember that the tag has to support the binding. This is not a general ColdFusion feature, but we can use it wherever we desire. On Page Binding We are going to bind 'cfdiv' to pull its content to show on page binding. We will set the value of a text input to the div. Refer to the following code. ColdFusion AJAX elements work in a manner different from how AJAX is written traditionally. It is more customary to name our browser-side HTML elements with id attributes. This is not the case with the binding features. As we can see in our code example, we have used the name attribute. We should remember to be case sensitive, since this is managed by JavaScript. When we run the code, we will notice that we must leave the input field before the browser registers that there has been a change in the value of the field. This is how the event model for the browser DOM works. <cfform id="myForm" format="html"> This is my edit box.<br /> <cfinput type="text" name="myText"></cfform><hr />And this is the bound div container.<br /><cfdiv bind="{myText}"></cfdiv> Notice how we use curly brackets to bind the value of the 'myText' input box. This inserts the contents into 'div' when the text box loses focus. This is an example of binding to in-page elements. If the binding we use is tied to a hidden window or tab, then the contents may not be updated. CFC Binding Now, we are going to bind our div to a CFC method. We will take the data that was being posted directly to the object, and then we will pass it out to the CFC. The CFC is going to repackage it, and send it back to the browser. The binding will enable the modified version of the content to be sent to the div. Refer to the following CFC code: <cfcomponent output="false"> <cffunction name="getDivContent" returntype="string" access="remote"> <cfargument name="edit"> <cfreturn "This is the content returned from the CFC for the div, the calling page variable is '<strong>#arguments.edit#</strong>'."> </cffunction></cfcomponent> From the previous code, we can see that the CFC only accepts the argument and passes it back. This could have even returned an image HTML segment with something like a user picture. The following code shows the new page code modifications. <cfform id="myForm" format="html"> This is my edit box.<br /> <cfinput type="text" name="myText"></cfform><hr />And this is the bound div container.<br /><cfdiv bind="cfc:bindsource.getDivContent({myText})"></cfdiv> The only change lies in how we bind the cfdiv element tag. Here, you can see that it starts with CFC. Next, it calls bindsource, which is the name of a local CFC. This tells ColdFusion to wire up the browser page, so it will connect to the CFC and things will work as we want. You can observe that inside the method, we are passing the bound variable to the method. When the input field changes by losing focus, the browser sends a new request to the CFC and updates the div. We need to have the same number of parameters going to the CFC as the number of arguments in our CFC method. We should also make sure that the method has its access method set to remote. Here we can see an example results page. It is valid to pass the name of the CFC method argument with the data value. This can prevent exceptions caused by not pairing the data in the same order as the method arguments. The last line of the previous code can be modified as follows: <cfdiv bind="cfc:bindsource.getDivContent(edit:{myText})"></cfdiv> JavaScript Binding Now, we will see how simple power can be managed on the browser. We will create a standard JavaScript function and pass the same bound data field through the function. Whenever we update the text box and it looses focus, the contents of the div will be updated from the function on the page. It is suggested that we include all JavaScript rather than put it directly on the page. Refer to the following code: <cfform id="myForm" format="html"> This is my edit box.<br /> <cfinput type="text" name="myText"></cfform><hr />And this is the bound div container.<br /><cfdiv bind="javascript:updateDiv({myText})"></cfdiv><script> updateDiv = function(myEdit){ return 'This is the result that came from the JavaScript function with the edit box sending "<strong>'+myEdit+'</strong>"'; } </script> Here is the result of placing the same text into our JavaScript example. URL Binding We can achieve the same results by calling a web address. We can actually call a static HTML page. Now, we will call a .cfm page to see the results of changing the text box reflected back, as for CFC and JavaScript. Here is the code for our main page with the URL binding. <cfform id="myForm" format="html"> This is my edit box.<br /> <cfinput type="text" name="myText"></cfform><hr />And this is the bound div container.<br /><cfdiv bind="url:bindsource.cfm?myEdit={myText}"></cfdiv> In the above code, we can see that the binding type is set to URL. Earlier, we used the CFC method bound to a file named bindsource.cfc. Now, we will bind through the URL to a .cfm file. The bound myText data will work in a manner similar to the other cases. It will be sent to the target; in this case, it is a regular server-side page. We require only one line. In this example, our variables are URL variables. Here is the handler page code: <cfoutput> 'This is the result that came from the server page with the edit box sending "<strong>#url.myEdit#</strong>"'</cfoutput> This tells us that if there is no prefix to the browse request on the bind attribute of the <cfdiv> tag, then it will only work with on-page elements. If we prefix it, then we can pass the data through a CFC, a URL, or through a JavaScript function present on the same page. If we bind to a variable present on the same page, then whenever the bound element updates, the binding will be executed. Bind with Event One of the features of binding that we might overlook its binding based on an event. In the previous examples, we mentioned that the normal event trigger for binding took place when the bound field lost its focus. The following example shows a bind that occurs when the key is released. <cfform id="myForm" format="html"> This is my edit box.<br /> <cfinput type="text" name="myText"></cfform><hr />And this is the bound div container.<br /><cfdiv bind="{myText@keyup}"></cfdiv> This is similar to our first example, with the only difference being that the contents of the div are updated as each key is pressed. This works in a manner similar to CFC, JavaScript, and URL bindings. We might also consider binding other elements on a click event, such as a radio button. The following example shows another feature. We can pass any DOM attribute by putting that as an item after the element id. It must be placed before the @ symbol, if you are using a particular event. In this code, we change the input in order to have a class in which we can pass the value of the class attribute and change the binding attribute of the cfdiv element. <cfform id="myForm" format="html"> This is my edit box.<br /> <cfinput type="text" name="myText" class="test"> </cfform><hr />And this is the bound div container.<br /><cfdiv bind="{myText.class@keyup}.{myText}"></cfdiv> Here is a list of the events that we can bind. @click @keyup @mousedown @none The @none event is used for grids and trees, so that changes don't trigger bind events. Extra Binding Notes If you have an ID on your CFForm element, then you can refer to the form element based on the container form. The following example helps us to understand this better. Bind = "url:bindsource.cfm?myEdit={myForm:myText}" The ColdFusion 8 documents give the following guides in order to specify the binding expressions. cfc: componentPath.functionName (parameters) The component path cannot use a mapping. The componentPath value must be a dot-delimited path from the web root or the directory that contains the page. javascript: functionName (parameters) url: URL?parameters ULR?parameters A string containing one or more instances of {bind parmeter}, such as {firstname}.{lastname}@{domain} The following table represents the supported formats based on attributes and tags: Attribute Tags Supported Formats Autosuggest cfinput type="text" 1,2,3 Bind cfdiv, cfinput, cftextarea 1,2,3,5 Bind cfajaxproxy, cfgrid, cfselect cfsprydataset, cftreeitem 1,2,3 onChange cfgrid 1,2,3 Source cflayoutarea, cfpod, cfwindow 4
Read more
  • 0
  • 0
  • 4890
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-podcasting-and-images-drupal
Packt
27 Oct 2009
8 min read
Save for later

Podcasting and Images in Drupal

Packt
27 Oct 2009
8 min read
Getting Started with Podcasts To create a podcast, you will need: A mp3 file A place to store the mp3 file At the risk of stating the obvious, a good podcast requires thought and planning before you make the actual recording. Later in the article, we will discuss some of these general mechanics. But, from a technical perspective, once you have your audio file, you can upload it to your Drupal site, and you will have published a podcast. Audio Module The Audio module supports the playback of audio files that have been uploaded to your site. To install this module, we will also need to install two helper modules required by the Audio module: the getID3() and Token modules. In this section, we will cover installing the Audio module, as well as the getID3() and Token modules. Install the getID3() Module Download the getID3() module from http://drupal.org/project/getid3, and upload it to your sites/all/modules directory . Do not, however, enable the module, as we need to install an additional piece of code described as follows. Install the getID3() Libraries The getID3() libraries are a tool that automatically extract information about audio files. These libraries don't require you to do any additional work; rather, they detect information that can be used by the Audio module. Download the getID3() libraries from http://getid3.sourceforge.net/. Unzip these libraries onto your hard drive. As shown in the preceding screenshot, the libraries include some demo and helper files, in addition to the readme and license files. The only files we need are contained in the getid3 directory. The getid3 directory is the only directory that you need to upload to your website. Then, use your FTP client to connect to your web server, and navigate to sites/all/modules/getid3. Upload the getid3 directory into sites/all/modules/getid3 as shown in the following screenshot: Once the module and the libraries have been uploaded to your site, enable the getID3() module by clicking the Administration | Site building | Modules link, or by navigating to admin/build/modules. Following these instructions the path to your getID3() library is sites/all/modules/getid3/getid3. If needed, this path can be adjusted at Administer | Site configuration | getID3(), or admin/settings/getid3. Install the Token Module Download the Token module from http://drupal.org/project/token, and install it. Once this module has been uploaded to your site, enable it by clicking the Administration | Site building | Modules link, or by navigating to admin/build/modules. The Token module is a helper module, and its functionality will be largely invisible to the end user. The Token module supplies pieces of text, or tokens, which can be used by other modules. The Audio module relies on the Token module and the getID3() module to help automatically generate titles and other information for audio files. Install and Enable the Audio Module Download the audio module from http://drupal.org/project/audio. Upload the module to your sites/all/modules directory, and enable it by clicking the Administer | Site building | Modules link or by navigating to admin/build/modules. Select the Audio and the Audio getID3 modules. Click the Save configuration button to submit the form and enable the modules. Configure the Audio Module Now that we have installed the Audio module and its helper modules, we need to configure the audio module to support our needs. Click the Administer | Site Configuration | Audio link, or navigate to admin/settings/audio. As pictured in the following screenshot, you will see three tabs across the top of the page: Audio, Metadata tags, and Players. The Audio Tab The options on the Audio tab, pictured in the preceding screenshot, allow you to set some default values that are used when audio posts are uploaded. The values here can be created automatically, which can be useful if you are working with songs. For most cases, however, you will want to delete the option for the Default node title format, and leave the other default values intact. When you have adjusted the settings, click the Save configuration button at the bottom of the page. To save your settings, you must click the Save configuration button before moving on to the next tab. A Brief Explanation of Tokens In the preceding screenshot, there is a collapsible fieldset titled List of available tokens. Click on the link to expand the fieldset. A portion of the tokens available are shown in the following screenshot: As suggested by the preceding screenshot, tokens expose pieces of information about content created within a site. Tokens can only be used when a module has been written to work with the tokens. Because the Audio module has been written to depend on the Token module, we have the option of using tokens if we wish. For example, we could set the title of audio nodes to automatically incorporate the username and the creation date. To make this work, we would set the Default node title format (as shown in the Audio settings screenshot) to Created by [author-name] on [yyyy]-[mon]-[date]. In most cases tokens run invisibly in the background without requiring any adjustments by the end user. The Metadata Tags Tab The options in this section will be useful if you are setting up podcasts as part of a music or radio station, but will be less useful in other environments. By reducing the number of required options, you can simplify the form for uploading podcasts. The settings pictured in the following screenshot are all you need to get started publishing audio on the web. The Players Tab The Audio module comes with several different players that can be used to play your audio files. You can use the settings on this page to choose your preferred player. As you can see in the following figure, you can specify a different player for each type of audio file. The "best" player will largely be determined by your aesthetic preference; all of the players do a great job playing audio stored on your site. After you have chosen a player, click the Save configuration button to save your preference. Assign Rights to the Audio Module Now that we have installed, enabled, and configured the audio module, we need to assign rights to it. Click the Administer | User management | Roles link, or navigate to admin/user/roles. The possible rights that can be assigned are shown in the following figure: We will need to assign rights for the teacher role, the student role, the authenticated user role, and possibly the anonymous user role. For the authenticated user role, assign rights to download audio and play audio. For the student role, assign rights to create audio and edit own audio. For the teacher role, assign rights to create audio, edit own audio, and view download stats. For the anonymous user role, assign the rights you think are appropriate. In most cases, if you are allowing anonymous users to see content, allowing them the rights to download audio and play audio is appropriate. Each time you assign rights to an individual roles, click the Save permissions button to save the rights for the role. Adjust Existing Views Currently, three views are being used to display student and teacher-created content. We will need to edit these views so that they return any audio nodes created within the site. To edit these views, click the Administer | Site building | Views link, or navigate to admin/build/views. We need to edit three views: the teacher_blog view, the student_blog and conversation views. As shown in the following screenshot, these views can be edited by using the Edit link on the main Views administration page. Editing the student_blog View Click the Edit link as shown in the preceding screenshot. Then, in the Defaults display, under Filters, click on the Node: Type link, as shown by Item 1 in the following screenshot: As shown by Item 2 in the preceding screenshot, add Audio to the node types returned in this view. Click the Update button to store this change, and then click the Save button (not pictured in the preceding screenshot) to save the view. Editing the conversations View Click the Edit link for the conversations view. Then, in the Defaults display, under Arguments, click on the Search:Links to link, as shown by Item 1 in the following figure: As shown by Item 2 in the preceding screenshot, add Audio to the list of node types where this view will be validated. Click the Update button to store this change, and then click the Save button to save the view. As we add additional content types into the site, we will need to update these views to account for the newly-added content types.
Read more
  • 0
  • 0
  • 1202

article-image-managing-orders-joomla-and-virtuemart
Packt
26 Oct 2009
6 min read
Save for later

Managing orders in Joomla! and VirtueMart

Packt
26 Oct 2009
6 min read
Our shop is now ready for customers. They can register to the shop and get some permissions to browse products and place orders. After building the catalog, one of the big tasks of the shop administrator is to manage the orders. Managing an order includes viewing the order, ensuring that payment is made, shipping the product to customers ship to address, and setting the appropriate status of the order. Whenever the status of the order changes, the shop administrator can also notify the customer about its status. When the product is delivered, the status should also be changed. Sometimes, you need to change the status when the customer refunds it for some reason. Viewing the orders To view the list of orders placed, click on Orders | List Orders. You get the Order List screen: The Order List screen shows all of the orders placed so far in that store. It shows the latest order first. As you can see, it shows the order number, name of customer, date of order, date last modified, status of the order, and total price of the order. As there may be hundreds of orders per day, you need to filter the orders and see which ones need urgent attention. You can filter the orders by their status. For example, clicking on the Pending link will show all of the orders which are pending. Viewing the list of pending orders, you may enquire why those are pending. Some may be pending for not making the payment, or you may be waiting for some offline payment. For example, when the Money Order payment method is used, the order needs to remain Pending until you receive the money order. Once you get the payment, you can change the order status to Confirmed. Viewing an order's details In the Order List screen, you will get an overview of each order. However, sometimes it may be necessary to view the details of an order. For viewing an order's details, in the Order List screen, click on the order number link under the Order Number column. This shows details of the order: In the Order Details page, you will first see the order number, order date, order status, its current status, and IP address from where the order was placed. There is a box section from where you can update the order's status and view the order's history. Then, you get the Bill To and Ship To addresses. After the Bill To and Ship To addresses, you get the list of ordered items and their prices. You can also add a new product to this order from this section. This section also shows taxes added, and shipping and handling fees: After the product items, you get another section which shows shipping information and payment method used: In the Shipping Information section, you get the carrier used, shipping mode applied, shipping price, shipping and handling fees, and shipping taxes. The payment section shows what method was used and when the payment was made. It shows the payment history for this order. It also shows how much of a coupon discount was applied to this order. As an administrator of the shop, you can change the values in the fields where an update icon () is displayed. At the bottom, you see the customer's comment. Customers may provide comments while placing the order. These comments may be very much valuable for the shop owner. For example, the customer may want the product to be delivered in a special way. The customer can express that in this comment. For printing the purchase orders, you may use a printer friendly view. To see the purchase order in a printer friendly view, click on the Print View link at top. This formats the purchase order as a plain document, and also shows a printer icon. Click on that printer icon to print the purchase order. Understanding an order's status How is the order management workflow maintained? Mainly, this is based on the order status. After receiving an order from the customer, it passes several statuses. An order's life cycle is shown in the following diagram: These order status types are defined in advance. At the very outset of starting the shop, the workflow should be clearly defined. Managing order status types You can view the existing order status types from Orders | List Order Status Types. This shows the List Order Status Types screen: As you see from the screen on the previous page, there are five status types. We may add another status type of Delivered. For adding a new order status type, click on the New icon in the toolbar, or on Orders | Add Order Status Type. Both brings the Order Status screen: In the Order Status screen, first type the Order Status Code. For the Delivered status, assign D as code. Then, type the name of the status type in the Order Status Name text box. In the Description text area, you may provide a brief description of the order status type. At the end, specify a list order value. Then, click on the Save icon in the toolbar. This adds the new Delivered order status type. You can create as many order status types as you need. Changing an order's status As indicated earlier, while fulfilling the order, the shop owner needs to update the status of the order, and communicate that status change to the customer. You can change an order's status from two places. In the Order List screen, you can see the orders and also change status. For changing the status of an order, select an order status type from drop-down list in the Status column. Then, click on the Update Status button to save the change. If you want to notify the customer about this     status change, select the Notify Customer? checkbox. One disadvantage of updating the order status from the Order List screen is that you cannot add a note on changing the status. The other way of updating the order status provides this advantage. For using this, click on the order number link in the Order List screen. The order details page will open. On the right side, you will see a box from where you can update the order status. Can you see the Comment text area in the following screen? As you can see, from the Order Status Change tab, you can change the status, write a comment, notify the customer about the status change, and can also add the comment with that notification.
Read more
  • 0
  • 0
  • 2553

article-image-article-personalize-your-pbx-using-freepbx-features
Packt
26 Oct 2009
4 min read
Save for later

Personalize Your Own PBX Using FreePBX Features

Packt
26 Oct 2009
4 min read
Let's get started. CallerID Lookup Sources Caller ID lookup sources supplement the caller ID name information that is sent by most telephone companies. A caller ID lookup source contains a list of phone numbers matched with names. When FreePBX receives a call, it can query a lookup source with the number of the caller. If the caller is on the lookup source's list, a name is returned that will be sent along with the call wherever the call gets routed to. The name will be visible on a phone's caller ID display (if the phone supports caller ID), and is also visible in the FreePBX call detail records. In order to set up a caller ID lookup source, click on the CallerID Lookup Sources link under the Inbound Call Control section of the navigation menu on the left side of the FreePBX interface as shown in the following screenshot: The Add Source screen has three common configuration options: Source Description Source type Cache results Source Description is used to identify this lookup source when it is being selected as a caller ID lookup source during the configuration of an inbound route. Source type is used to select the method that this source will use to obtain caller ID name information. FreePBX allows a lookup source to use one of the following methods: ENUM: FreePBX will use whichever ENUM servers are configured in /etc/asterisk/enum.conf to return caller ID name information. By default, this file contains the e164.arpa and e164.org zones for lookups. All ENUM servers in the enum.conf file will be queried. HTTP: FreePBX will query a web service for caller ID name information using the HTTP protocol. A lookup source that uses HTTP to query for information can use services such as Google Phonebook or online versions of the white/yellow pages to return caller ID names. When HTTP is selected as the source type, six additional options will appear for configuration. These options are discussed in the HTTP source type section. MySQL: FreePBX will connect to a MySQL database to query for caller ID name information. Usually, this will be a database belonging to a Customer Relationship Management (CRM) software package in which all customer information is stored. When MySQL is selected as the Source type, five additional options will appear for configuration. These options are discussed later in the MySQL source type section. SugarCRM: As of FreePBX version 2.5.1, this option is not yet implemented. In the future, this Source type option will allow FreePBX to connect to the database used by the SugarCRM software package to query for caller ID name information. If the Cache results checkbox is selected, then when a lookup source returns results they will be cached in the local AstDB database for quicker retrieval the next time the same number is looked up. Note that values cached in the AstDB will persist past a restart of Asterisk and a reboot of the PBX. Once a caller ID name has been cached, FreePBX will always return that name even if the name in the lookup source changes. Caching must be disabled for a new caller ID name to be returned from the lookup source. Once all configuration options have been filled out, click on the Submit Changes button followed by the orange-colored Apply Configuration Changes bar to make the new lookup source available to inbound routes. Now that we have an available lookup source, we can configure an inbound route to use this source to set caller ID information. Click on the Inbound Routes link under the Inbound Call Control section of the navigation menu on the left side of the FreePBX interface as shown in the following screenshot: Click the name of the inbound route that will use the new lookup source in the menu on the right side of the page (in this example, DID 5551234567) as shown in the following screenshot: Scroll down the page to the CID Lookup Source section. Select the name of the new lookup source from the Source drop-down menu: Click on the Submit button at the bottom of the page, followed by the orange-colored Apply Configuration Changes bar at the top of the page. Calls that are routing using this inbound route will now query our new lookup source for caller ID name information.
Read more
  • 0
  • 0
  • 878

article-image-development-windows-mobile-applications-part-1
Packt
26 Oct 2009
4 min read
Save for later

Development of Windows Mobile Applications (Part 1)

Packt
26 Oct 2009
4 min read
Windows OS for Windows Mobile is available in various versions, but for this article we will be using Windows Mobile 6. Windows Mobile 6 uses .NET Compact Framework v2 SP2, and has 3 different versions: Windows Mobile 6 Standard (phones without Touch Screen) Windows Mobile 6 Professional (with Phone functionality) Windows Mobile 6 Classic (without Phone functionality) Windows Mobile 6.1 and Windows Mobile 6.5 are other 2 higher versions available with some additional features as compared to Windows Mobile 6. Windows Mobile 7 expected to be released in 2010 and is said to have major updates. This article concentrates on development on Windows Mobile 6 Professional. Software Prerequisite This article will introduce you to the development for Windows Mobile 6 Professional, using Visual C#. Windows Mobile 6 has .NET Compact Framework v2 SP2 preinstalled. .NET Compact Framework is a Compact Edition of .NET Framework, and does not have all the features of the complete .NET Framework. Following are the software required for development: Microsoft Visual Studio 2008 Windows Mobile 6 Professional SDK Refresh (SDK contains emulator, used for testing, debugging. To download click here) ActiveSync (Used for Data Synchronizing between development machine and Windows Mobile, To download click here) Without making any exception, we will follow the golden rule of learning by writing “Hello World” application. Hello World We will assume that you have installed all the prerequisite software mentioned above. Launch Visual Studio 2008 and select Visual C# (if prompted). Create a new Project (File -> New) as shown below: While creating a new Project, Visual Studio 2008 IDE provides an option to select an installed template, which will create Project with all the basic requirements/structure for development. For Windows Mobile, we will select option Smart Device and template Smart Device Project as shown below.  You can also provide: Name: Name for Project. We will call it as MyFirstApp. Location: Location where this project will be created. Browse and set the desired location. We will use default location for now. Solution Name: Name for referring the Solution. Usually we keep it same as Project Name. Since Windows Mobile 6 has .NET Compact Framework v2, it will select the .NET Framework 2.0 from the dropdown on the top right. Click OK. Next step is to select the Target platform, .NET Compact Framework Version and Template. For our application we will select: Target platform: Windows Mobile 6 Professional SDK .NET Compact Framework Version: .NET Compact Framework Version 2.0. Template: Device Application Project MyFirstApp is successfully created and IDE will open a Form as shown. Let me introduce you to the various sections on screen. This is the main section called development section. All the coding and designing of the Form is done here. This section is called Toolbox and lists all the available components. If this section is not visible click View->Toolbox. This section is called Solution Explorer and shows all the forms, resources and properties. If this section is not visible click View->Solution Explorer. This section is Properties and displays all the properties for the component selected. If this section is not visible click View->Properties Window. By default Form is named as Form1. Let us first change the Name of the form. To do so select the form and the properties related to form will be listed in properties window. The entire properties list is in the form of Key Value pair. For changing Name of form, change value of property Name. For this example we will change it to HelloWorldForm. Now this form will be referred as HelloWorldForm throughout the application. Changing form name doesn’t change form caption (title) it is still showing Form1. To change caption change the value of property name Text. For this example we will change the Text to Hello World. Also the file representing this form in Solution Explorer will still be referred as Form1.cs, again you can either keep the name of the file as it is or can rename it. We will rename it to HelloWorld.cs.
Read more
  • 0
  • 0
  • 4298
article-image-trunks-freepbx-25
Packt
26 Oct 2009
5 min read
Save for later

Trunks in FreePBX 2.5

Packt
26 Oct 2009
5 min read
A trunk in the simplest of terms is a pathway into or out of a telephone system. A trunk connects a PBX to outside resources, such as PSTN telephone lines, or additional PBX systems to perform inter-system transfers. Trunks can be physical, such as a PRI or PSTN line, or they can be virtual by routing calls to another endpoint using Internet Protocol (IP) links. Trunk types FreePBX allows the creation of six different types of trunks as follows: Zap IAX2 SIP ENUM DUNDi Custom Zap, IAX2, and SIP trunks utilize the technologies of their namesake. These trunks have the same highlights and pitfalls that extensions and devices using the same technology do. Zap trunks require physical hardware cards for incoming lines to plug into. SIP trunks are the most widely adopted and compatible, but have difficulties traversing firewalls. IAX2 trunks are able to traverse most firewalls easily, but are limited to adoption mainly on Asterisk-based systems. In terms of VoIP, ENUM(E.164 NUmber Mapping) is a method for unifying E.164 (the international telecommunication numbering plan) with VoIP routing. The ENUM system can be considered very similar to the way that the Internet DNS system works. In the DNS system, when a domain name is looked up an IP address is returned. The IP address allows a PC to traverse the Internet and find the server that belongs to that IP address. The ENUM system provides VoIP routes back when queried for a phone number. The route that is returned is usually a SIP or IAX2 route. An ENUM trunk allows FreePBX to send the dialed phone number to the publice164.orgENUM server. If the called party has listed their phone number in the e164.org directory, a VoIP route will be returned and the call will be connected using that route. A VoIP route contains the VoIP protocol, the server name or IP address, the port, and the extension to use in order to contact the dialed phone number. For example, a SIP route for dialing the number 555-555-1234 might appear as SIP:[email protected]:5060. This is advantageous in several ways. It is important to note that indirect routes to another telephony system are often costly. Calling a PSTN telephone number typically requires that call to route through a third-party provider's phone lines and switching equipment (a service they will happily charge for). If a number is listed in the ENUM directory, the returned route will bridge the call directly to the called party (or their provider), bypassing the cost of routing through a third party. ENUM also benefits the called party, allowing them to redirect inbound calls to wherever they would like. Service disruptions that would otherwise render a particular phone number useless can be bypassed by directing the phone number to a different VoIP route in the ENUM system. More information on ENUM can be found at the following web sites: The ENUM home page The e164.org home page: The Internet Engineering Task Force ENUM charter DUNDi (Distributed Universal Number Discovery) is a routing protocol technology similar to ENUM. In order to query another Asterisk system using DUNDi, that system must be "peered" with your own Asterisk system. Peering requires generating and exchanging key files with the other peer. DUNDi is a decentralized way of accomplishing ENUM-style lookups. By peering with one system you are effectively peering with any other system that your peer is connected to. If system A peers with system B, and system B peers with system C, then system C will be able to see the routes provided by system A. In peer-topeer fashion, system B will simply pass the request along to system A, even though system C has no direct connection to system A. DUNDi is not limited to E.164 numbering schemes like ENUM and it allows a PBX to advertise individual extensions, or route patterns, instead of whole phone numbers. Therefore, it is a good candidate for distributed office setups, where a central PBX can be peered with several satellite PBX systems. The extensions on each system will be able to call one another directly without having to statically set up routes on each individual PBX. More information on DUNDi can be found at the following web sites: DUNDi home page Example DUNDi SIP configuration Example DUNDi IAX2 configuration Custom trunks work in the same fashion as custom extensions do. Any valid Asterisk Dial command can be used as a custom trunk by FreePBX. Custom trunks typically use additional VoIP protocols such as H.323 and MGCP. Setting up a new trunk Setting up a trunk in FreePBX is very similar to setting up an extension. All of the trunks share eight common setup fields, followed by fields that are specific to the technology that trunk will be using. In order to begin setting up a trunk, click on Trunks in the left side navigation menu as shown in the following screenshot: From the Add a Trunk screen, click on the name of the technology that the trunk will be using (for example, if a SIP trunk will be used, click on Add SIP Trunk) as shown in the following screenshot:
Read more
  • 0
  • 0
  • 2963

article-image-development-windows-mobile-applications-part-2
Packt
26 Oct 2009
3 min read
Save for later

Development of Windows Mobile Applications (Part 2)

Packt
26 Oct 2009
3 min read
Now let us see how to deploy it on Windows Mobile Device. For deploying the application on device you need to have ActiveSync installed. There are two ways in which application can be deployed on to the device.  First option is to connect the device to the Development machine via USB. ActiveSync will automatically detect it and you can click on on the top bar. And this time select option "Windows Mobile 6 Professional Device". But then this approach is useful when you want to test/deploy and use the application yourself. What if you want to distribute it to others? In that case you need to create an installation program for your Windows mobile application. The installation file in the Windows Mobile world is distributed in the form of a CAB file. So once we have done with application we should opt for option 2 of creating a CAB file (A CAB file is a library of compressed files stored as a single file). Creating CAB File Creating a CAB file itself is a new project. To create CAB project right click on the solution and select the option New Project as shown below. Clicking on New Project option will open Add New Project Wizard. Select option Setup and Deployment under Other Project Types on the left hand menu. Then select option Smart Device CAB Project on right hand side as shown below. We have named this project as MyFirstAppCAB. Click OK and MyFirstAppCAB project is created under the solution a shown. Now to add files to the CAB, right click on the Application Folder under File System on Target Machine and select option Add-> Project Output as shown. On selecting Project Output option, the following screen will popup. Depending upon the requirement, select what all needs to be compressed in CAB file. For this example we require only output, hence will select option Primary output. Now right click on CAB project MyFirstAppCAB and select option Build. CAB file with name MyFirstAppCAB will be created at the location MyFirstAppMyFirstAppCABDebug. Now let us see how we can deploy this CAB file on emulator and run the application. Click on Tools on the top bar and select option Device Emulator Manager. This will open a Device Emulator Manager as shown below. Select option Windows Mobile 6 Classic Emulator. Right click and select option Connect. Windows Mobile Emulator will start and on Device Emulator Manager you can see to left of Windows Mobile 6 Classic Emulator as shown below. Next step is to Cradle. If you are attempting to cradle for the 1st time, then you need to setup ActiveSync. To setup ActiveSync, double click on on right bottom on Task bar. Microsoft ActiveSync will open up, select option File -> Connection Settings as shown in figure below. Connection Settings window will be open up as shown below. Check the option Allow Connections to one of the followings and then from the drop down select the option DMA. After connecting Windows Mobile 6 Classic Emulator using Device Emulator Manager, again right click and select option Cradle as shown below. Cradle will start ActiveSync and make Emulator work as device connected using ActiveSync. On successful connection you can see on the left of option Windows Mobile 6 Classic Emulator on Device Emulator Manager as shown.
Read more
  • 0
  • 0
  • 2171

article-image-manipulating-jquery-tables
Packt
24 Oct 2009
20 min read
Save for later

Manipulating jQuery tables

Packt
24 Oct 2009
20 min read
In this article by Karl Swedberg and Jonathan Chaffer, we will use an online bookstore as our model website, but the techniques we cook up can be applied to a wide variety of other sites as well, from weblogs to portfolios, from market-facing business sites to corporate intranets. In this article, we will use jQuery to apply techniques for increasing the readability, usability, and visual appeal of tables, though we are not dealing with tables used for layout and design. In fact, as the web standards movement has become more pervasive in the last few years, table-based layout has increasingly been abandoned in favor of CSS‑based designs. Although tables were often employed as a somewhat necessary stopgap measure in the 1990s to create multi-column and other complex layouts, they were never intended to be used in that way, whereas CSS is a technology expressly created for presentation. But this is not the place for an extended discussion on the proper role of tables. Suffice it to say that in this article we will explore ways to display and interact with tables used as semantically marked up containers of tabular data. For a closer look at applying semantic, accessible HTML to tables, a good place to start is Roger Johansson's blog entry, Bring on the Tables at www.456bereastreet.com/archive/200410/bring_on_the_tables/. Some of the techniques we apply to tables in this article can be found in plug‑ins such as Christian Bach's Table Sorter. For more information, visit the jQuery Plug‑in Repository at http://jQuery.com/plugins. Sorting One of the most common tasks performed with tabular data is sorting. In a large table, being able to rearrange the information that we're looking for is invaluable. Unfortunately, this helpful operation is one of the trickiest to put into action. We can achieve the goal of sorting in two ways, namely Server-Side Sorting and JavaScript Sorting. Server-Side Sorting A common solution for data sorting is to perform it on the server side. Data in tables often comes from a database, which means that the code that pulls it out of the database can request it in a given sort order (using, for example, the SQL language's ORDER BY clause). If we have server-side code at our disposal, it is straightforward to begin with a reasonable default sort order. Sorting is most useful when the user can determine the sort order. A common idiom is to make the headers of sortable columns into links. These links can go to the current page, but with a query string appended indicating the column to sort by: <table id="my-data">   <tr>     <th class="name"><a href="index.php?sort=name">Name</a></th>     <th class="date"><a href="index.php?sort=date">Date</a></th>   </tr>   ... </table> The server can react to the query string parameter by returning the database contents in a different order. Preventing Page Refreshes This setup is simple, but requires a page refresh for each sort operation. As we have seen, jQuery allows us to eliminate such page refreshes by using AJAX methods. If we have the column headers set up as links as before, we can add jQuery code to change those links into AJAX requests: $(document).ready(function() {   $('#my-data .name a').click(function() {     $('#my-data').load('index.php?sort=name&type=ajax');     return false;   });   $('#my-data .date a').click(function() {     $('#my-data').load('index.php?sort=date&type=ajax');     return false;   }); }); Now when the anchors are clicked, jQuery sends an AJAX request to the server for the same page. We add an additional parameter to the query string so that the server can determine that an AJAX request is being made. The server code can be written to send back only the table itself, and not the surrounding page, when this parameter is present. This way we can take the response and insert it in place of the table. This is an example of progressiveenhancement. The page works perfectly well without any JavaScript at all, as the links for server-side sorting are still present. When JavaScript is present, however, the AJAX hijacks the page request and allows the sort to occur without a full page load. JavaScript Sorting There are times, though, when we either don't want to wait for server responses when sorting, or don't have a server-side scripting language available to us. A viable alternative in this case is to perform the sorting entirely on the browser using JavaScript client-side scripting. For example, suppose we have a table listing books, along with their authors, release dates, and prices: <table class="sortable">   <thead>     <tr>       <th></th>       <th>Title</th>       <th>Author(s)</th>       <th>Publish&nbsp;Date</th>       <th>Price</th>     </tr>   </thead>   <tbody>     <tr>       <td>         <img src="../covers/small/1847192386.png" width="49"              height="61" alt="Building Websites with                                                 Joomla! 1.5 Beta 1" />       </td>       <td>Building Websites with Joomla! 1.5 Beta 1</td>       <td>Hagen Graf</td>       <td>Feb 2007</td>       <td>$40.49</td>     </tr>     <tr>       <td><img src="../covers/small/1904811620.png" width="49"                height="61" alt="Learning Mambo: A Step-by-Step                Tutorial to Building Your Website" /></td>       <td>Learning Mambo: A Step-by-Step Tutorial to Building Your           Website</td>       <td>Douglas Paterson</td>       <td>Dec 2006</td>       <td>$40.49</td>     </tr>     ...   </tbody> </table> We'd like to turn the table headers into buttons that sort by their respective columns. Let us look into ways of doing this.   Row Grouping Tags Note our use of the <thead> and <tbody> tags to segment the data into row groupings. Many HTML authors omit these implied tags, but they can prove useful in supplying us with more convenient CSS selectors to use. For example, suppose we wish to apply typical even/odd row striping to this table, but only to the body of the table: $(document).ready(function() {   $('table.sortable tbody tr:odd').addClass('odd');   $('table.sortable tbody tr:even').addClass('even'); }); This will add alternating colors to the table, but leave the header untouched: Basic Alphabetical Sorting Now let's perform a sort on the Titlecolumn of the table. We'll need a class on the table header cell so that we can select it properly: <thead>   <tr>     <th></th>    <th class="sort-alpha">Title</th>     <th>Author(s)</th>     <th>Publish&nbsp;Date</th>     <th>Price</th>   </tr> </thead> To perform the actual sort, we can use JavaScript's built in .sort()method. It does an in‑place sort on an array, and can take a function as an argument. This function compares two items in the array and should return a positive or negative number depending on the result. Our initial sort routine looks like this: $(document).ready(function() {   $('table.sortable').each(function() {     var $table = $(this);     $('th', $table).each(function(column) {       if ($(this).is('.sort-alpha')) {         $(this).addClass('clickable').hover(function() {           $(this).addClass('hover');         }, function() {           $(this).removeClass('hover');         }).click(function() {           var rows = $table.find('tbody > tr').get();           rows.sort(function(a, b) {             var keyA = $(a).children('td').eq(column).text()                                                       .toUpperCase();             var keyB = $(b).children('td').eq(column).text()                                                       .toUpperCase();             if (keyA < keyB) return -1;             if (keyA > keyB) return 1;             return 0;           });           $.each(rows, function(index, row) {             $table.children('tbody').append(row);           });         });       }     });   }); }); The first thing to note is our use of the .each() method to make iteration explicit. Even though we could bind a click handler to all headers with the sort-alpha class just by calling $('table.sortable th.sort-alpha').click(), this wouldn't allow us to easily capture a crucial bit of information&#x97;the column index of the clicked header. Because .each() passes the iteration index into its callback function, we can use it to find the relevant cell in each row of the data later. Once we have found the header cell, we retrieve an array of all of the data rows. This is a great example of how .get()is useful in transforming a jQuery object into an array of DOM nodes; even though jQuery objects act like arrays in many respects, they don't have any of the native array methods available, such as .sort(). With .sort() at our disposal, the rest is fairly straightforward. The rows are sorted by comparing the textual contexts of the relevant table cell. We know which cell to look at because we captured the column index in the enclosing .each() call. We convert the text to uppercase because string comparisons in JavaScript are case-sensitive and we wish our sort to be case-insensitive. Finally, with the array sorted, we loop through the rows and reinsert them into the table. Since .append() does not clone nodes, this moves them rather than copying them. Our table is now sorted. This is an example of progressive enhancement's counterpart, gracefuldegradation. Unlike with the AJAX solution discussed earlier, we cannot make the sort work without JavaScript, as we are assuming the server has no scripting language available to it in this case. The JavaScript is required for the sort to work, so by adding the "clickable" class only through code, we make sure not to indicate with the interface that sorting is even possible unless the script can run. The page degrades into one that is still functional, albeit without sorting available. We have moved the actual rows around, hence our alternating row colors are now out of whack: We need to reapply the row colors after the sort is performed. We can do this by pulling the coloring code out into a function that we call when needed: $(document).ready(function() {   var alternateRowColors = function($table) {     $('tbody tr:odd', $table).removeClass('even').addClass('odd');     $('tbody tr:even', $table).removeClass('odd').addClass('even');   };     $('table.sortable').each(function() {     var $table = $(this);     alternateRowColors($table);     $('th', $table).each(function(column) {       if ($(this).is('.sort-alpha')) {         $(this).addClass('clickable').hover(function() {           $(this).addClass('hover');         }, function() {           $(this).removeClass('hover');         }).click(function() {           var rows = $table.find('tbody > tr').get();           rows.sort(function(a, b) {             var keyA = $(a).children('td').eq(column).text()                                                       .toUpperCase();             var keyB = $(b).children('td').eq(column).text()                                                       .toUpperCase();             if (keyA < keyB) return -1;             if (keyA > keyB) return 1;             return 0;           });           $.each(rows, function(index, row) {             $table.children('tbody').append(row);           });           alternateRowColors($table);         });       }     });   }); }); This corrects the row coloring after the fact, fixing our issue:   The Power of Plug-ins The alternateRowColors()function that we wrote is a perfect candidate to become a jQuery plug-in. In fact, any operation that we wish to apply to a set of DOM elements can easily be expressed as a plug-in. In this case, we only need to modify our existing function a little bit: jQuery.fn.alternateRowColors = function() {   $('tbody tr:odd', this).removeClass('even').addClass('odd');   $('tbody tr:even', this).removeClass('odd').addClass('even');   return this; }; We have made three important changes to the function. It is defined as a new property of jQuery.fn rather than as a standalone function. This registers the function as a plug-in method. We use the keyword this as a replacement for our $table parameter. Within a plug-in method, thisrefers to the jQuery object that is being acted upon. Finally, we return this at the end of the function. The return value makes our new method chainable. More information on writing jQuery plug-ins can be found in Chapter 10 of our book Learning jQuery. There we will discuss making a plug-in ready for public consumption, as opposed to the small example here that is only to be used by our own code. With our new plug-in defined, we can call $table.alternateRowColors(), which is a more natural jQuery syntax, intead of alternateRowColors($table). Performance Concerns Our code works, but is quite slow. The culprit is the comparator function, which is performing a fair amount of work. This comparator will be called many times during the course of a sort, which means that every extra moment it spends on processing will be magnified. The actual sort algorithm used by JavaScript is not defined by the standard. It may be a simple sort like a bubble sort (worst case of Θ(n2) in computational complexity terms) or a more sophisticated approach like quick sort (which is Θ(n log n) on average). In either case doubling the number of items increases the number of times the comparator function is called by more than double. The remedy for our slow comparator is to pre-compute the keys for the comparison. We begin with the slow sort function: rows.sort(function(a, b) {   keyA = $(a).children('td').eq(column).text().toUpperCase();   keyB = $(b).children('td').eq(column).text().toUpperCase();   if (keyA < keyB) return -1;   if (keyA > keyB) return 1;   return 0; }); $.each(rows, function(index, row) {   $table.children('tbody').append(row); }); We can pull out the key computation and do that in a separate loop: $.each(rows, function(index, row) {   row.sortKey = $(row).children('td').eq(column).text().toUpperCase(); }); rows.sort(function(a, b) {   if (a.sortKey < b.sortKey) return -1;   if (a.sortKey > b.sortKey) return 1;   return 0; }); $.each(rows, function(index, row) {   $table.children('tbody').append(row);   row.sortKey = null; }); In the new loop, we are doing all of the expensive work and storing the result in a new property. This kind of property, attached to a DOM element but not a normal DOM attribute, is called an expando.This is a convenient place to store the key since we need one per table row element. Now we can examine this attribute within the comparator function, and our sort is markedly faster.  We set the expando property to null after we're done with it to clean up after ourselves. This is not necessary in this case, but is a good habit to establish because expando properties left lying around can be the cause of memory leaks. For more information, see Appendix C.   Finessing the Sort Keys Now we want to apply the same kind of sorting behavior to the Author(s) column of our table. By adding the sort-alpha class to its table header cell, the Author(s)column can be sorted with our existing code. But ideally authors should be sorted by last name, not first. Since some books have multiple authors, and some authors have middle names or initials listed, we need outside guidance to determine what part of the text to use as our sort key. We can supply this guidance by wrapping the relevant part of the cell in a tag: <tr>   <td>     <img src="../covers/small/1847192386.png" width="49" height="61"             alt="Building Websites with Joomla! 1.5 Beta 1" /></td>   <td>Building Websites with Joomla! 1.5 Beta 1</td>   <td>Hagen <span class="sort-key">Graf</span></td>   <td>Feb 2007</td>   <td>$40.49</td> </tr> <tr>   <td>     <img src="../covers/small/1904811620.png" width="49" height="61"          alt="Learning Mambo: A Step-by-Step Tutorial to Building                                                 Your Website" /></td>   <td>     Learning Mambo: A Step-by-Step Tutorial to Building Your Website   </td>   <td>Douglas <span class="sort-key">Paterson</span></td>   <td>Dec 2006</td>   <td>$40.49</td> </tr> <tr>   <td>     <img src="../covers/small/1904811299.png" width="49" height="61"                   alt="Moodle E-Learning Course Development" /></td>   <td>Moodle E-Learning Course Development</td>   <td>William <span class="sort-key">Rice</span></td>   <td>May 2006</td>   <td>$35.99</td> </tr> Now we have to modify our sorting code to take this tag into account, without disturbing the existing behavior for the Titlecolumn, which is working well. By prepending the marked sort key to the key we have previously calculated, we can sort first on the last name if it is called out, but on the whole string as a fallback: $.each(rows, function(index, row) {   var $cell = $(row).children('td').eq(column);   row.sortKey = $cell.find('.sort-key').text().toUpperCase()                                   + ' ' + $cell.text().toUpperCase(); }); Sorting by the Author(s)column now uses the last name:     If two last names are identical, the sort uses the entire string as a tiebreaker for positioning.
Read more
  • 0
  • 0
  • 14939
article-image-creating-and-managing-user-groups-joomla-and-virtuemart
Packt
24 Oct 2009
9 min read
Save for later

Creating and Managing User Groups in Joomla! and VirtueMart

Packt
24 Oct 2009
9 min read
User manager In Joomla!, there is one User Manager component from where you can manage the users of that site. However, for the VirtueMart component, there is another  user manager which should be used for the VirtueMart shop. To be clear about  the differences of these two user managers, let us look into both. Joomla! user manager Let us first try Joomla!'s user manager. Go to the Joomla! control panel and click on the User Manager icon or click on Site | User Manager. This brings the User Manager screen of Joomla!: We see that the users registered to the Joomla! site are listed in this screen. This screen shows the username, full name, enabled status, group that the user is assigned to, email of the user, date and time when they last visited, and user ID. From this screen, you may guess that any user can be enabled or disabled by clicking on the icon in the Enabled column. Enabled user accounts show a green tick mark in the Enabled column. For viewing the details of any user, click on that user's name in the Name column. That brings up the User:[Edit] screen: As you see, the User Details section shows some important information about the user including Name, Username, E-mail, Group, and so on. You can edit and change these settings including the password. In the Group selection box, you must select one level. The deepest level gets the highest permission in the system. From this section, you can also block a user and decide whether they will receive system  emails or not. In the Parameters section, you can choose the Front-end Language and Time Zone for that user. If you have created contact items using Joomla!'s Contacts component, you may assign one contact to this user in the Contact Information section. VirtueMart user manager Let us now look into VirtueMart's user manager. From the Joomla! control panel, select Components | VirtueMart to reach the VirtueMart Administration Panel. To view the list of the user's registered to the VirtueMart store, click on Admin | Users. This brings the User List screen: As you can see, the User List screen shows the list of users registered to the shop. The screen shows their username, full name, group the user is assigned to, and their shopper group. In the Group column, note that there are two groups mentioned. One group is without brackets and another is inside brackets. The group name mentioned inside brackets is Joomla!'s standard user groups, whereas the one without brackets is VirtueMart's user group. We are going to learn about these user groups in the  next section. For viewing the details of a user, click on the user's name in Username column. That brings the Add/Update User Information screen: The screen has three tabs: General User Information, Shopper Information, and Order List. The General User Information tab contains the same information which was shown in Joomla!'s user manager's User: [Edit] screen. The Shopper Information tab contains shop related information for the user: The Shopper Information section contains: a vendor to which the user is registered the user group the user belongs to a customer number/ID the shopper group Other sections in this tab are: Shipping Addresses, Bill To Information, Bank Account, and any other section you have added to the user registration or account maintenance form. These sections contain fields which are either available on the registration or account maintenance form. If the user has placed some orders, the Order List tab will list the orders placed by that user. If no order has been placed,  the Order List tab will not be visible. Which user manager should we use? As we can see, there is a difference between Joomla!'s user manager and VirtueMart's user manager. VirtueMart's user manager shows some additional information fields, which are necessary for the operation of the shop. Therefore, whenever you are managing users for your shop, use the user manager in the VirtueMart component, not Joomla!'s user manager. Otherwise, all customer information will not be added or updated. This may create some problems in operating the VirtueMart store. User Groups Do you want to decide who can do what in your shop? There is a very good way for doing that in Joomla! and VirtueMart. Both Joomla! and VirtueMart have some predefined user groups. In both cases, you can create additional groups and assign permission levels to these groups. When users register to your site, you assign them to one of the user groups. Joomla! user groups Let us first look into Joomla! user groups. Predefined groups in Joomla! are  described below: User Group Permissions Public Frontend Registered Users in this group can login to the Joomla! site and view the contents, sections, categories, and the items which are marked only for registered users. This group has no access to content management. Author Users in this group get all the permissions the Registered group has. In addition to that, users in this group can submit articles for publishing, and can edit their own articles. Editor Users of this group have all the above permissions, and also can edit articles submitted by other users. However, they cannot publish the contents. Publisher Users in this group can login to the system and submit, edit, and publish their own content as well as contents submitted by other users. Public Backend Manager Users in this group can login to the administration panel and manage content items including articles, sections, categories, links, and so on. They cannot manage users, install modules or components, manage templates and languages, and access global configurations. Users in this group can access some of the components for which the administrator has given permission. Administrator In addition to content management, users in this group can add a user to Super Administrator group, edit a user, access the global configuration settings, access the mail function, and manage/install templates and language files. Super Administrator Users in this group can access all administration functions. For every site, at least one should be in this group to perform global configurations. You cannot delete a user in this group or move him/her to another group. As you can see, most of the users registering to your site should be assigned to the Registered group. By default, Joomla! assigns all newly registered users to the Registered group. You need to add some users to the Editor or Publisher group if they need to add or publish content to the site. The persons who are managing the shop should be assigned to other Public Backend groups such as Manager, Administrator or Super Administrator. VirtueMart user groups Let us now look into the user groups in VirtueMart. To see the user groups, go to VirtueMart's administration panel and click on Admin | User Groups. This shows the User Group List screen: By default, you will see four user groups: admin, storeadmin, shopper, and demo. These groups are used for assigning permissions to users. Also, note the values in the User Group Level column. The higher the value in this field, the lower the permissions assumed for the group. The admin group has a level value of 0, which means it has all of the permissions, and of course, more than the next group storeadmin. Similarly, storeadmin group has more permissions than the shopper group. These predefined groups are key groups in VirtueMart, and you cannot modify or delete these groups. These groups have the following permissions: Group Permissions admin This group has permissions to use all of the modules except checkout and shop. The admin group does not need these because admin users usually do not shop in their store. storeadmin This group has fewer permissions than admin group. Users in this group can access all the modules except the admin, vendor, shop, and checkout modules. They cannot set the global configurations for the store, but can add and edit payment methods, products, categories, and so on. shopper This group has the least permission among the three key groups. By default, users registered to the shop are assigned to this group. Users in this group can fully access the account module, and can use some functions of the shop, coupon, and checkout modules. demo This is a demo group created by default so that administrators can test and play with it. For most of the shops, these four predefined groups will be enough to implement appropriate permissions. However, in some cases you may need to create a new user group and assign separate permissions to that group. For example, you may want to employ some people as store managers who will add products to the catalog and manage the orders. They cannot add or edit payment methods, shipping methods, or other settings, except product and orders. If you add these people to the storeadmin group then they get more permissions than required. In such situations, a good solution is to create a new group, add selected user accounts to that group, and assign permissions to that group. Creating a new user group For creating a new user group, click on the New button in the toolbar on the User Group List screen. This brings Add/Edit a User Group screen: In the Add/Edit a User Group screen, enter the group's name and group level. You must type a higher value than existing groups (for example, 1000). Click on the Save icon to save the user group. You will now see the newly created user group in the User Group List screen.
Read more
  • 0
  • 0
  • 7668

article-image-custom-data-readers-ext-js
Packt
24 Oct 2009
9 min read
Save for later

Custom Data Readers in Ext JS

Packt
24 Oct 2009
9 min read
When writing Chapter 12, "It's All about the Data," of Learning Ext JS, I switched things up a bit and switched the server-side processes to utilizing Adobe's ColdFusion application server, instead of the PHP we had been using in the rest of the book. There were a few reasons we decided to do this. To show that Ext JS can work with any server-side technology. ColdFusion 8 includes Ext JS 1.1 for it's new Ajax form components. Adobe uses a custom format for the serialized JSON return of query data, making it perfect for our example needs. I'm a ColdFusion programmer. Some time ago, before writing Chapter 12, I had begun to use a Custom Data Reader that I had found on the Ext JS forums. Another Ext user and ColdFusion programmer, John Wilson, had written the custom reader to consume Adobe's custom JSON return for queries. First, let me show you why Adobe's format differs from the generally expected serialized JSON return of a query. Here's an example of a typical query response. { 'results': 2, 'rows': [ { 'id': 1, 'firstname': 'Bill', occupation: 'Gardener' }, // a row object { 'id': 2, 'firstname': 'Ben' , occupation: 'Horticulturalist' } // another row object ] } And here's an example of how ColdFusion returns a query response.     {        "COLUMNS":["INTPROPERTIESID","STRDEVELOPMENT","INTADDRESSID", "STRSTREET","STRSTREET2", "STRCITY","CHSTATEID","INTZIP"],        "DATA":[            [2,"Abbey Road",6,"456 Abbey Road","Villa 5","New York","NY",12345],            [6,"Splash",39,"566 aroundthe bend dr",null,"Nashville","TN",37221]        ]    } You can see, when examining the two formats that they are very divergent. The typical format returns an array of row objects of the query's results, whereas ColdFusion's format is an array (DATA) of arrays (each row of the query result), with each row array only containing the data. The ColdFusion format has extracted the column names into it's own array (COLUMNS), as opposed to the name/value pairing found in the object notation of the typical return. It's actually very smart, on Adobe's part, to return the data in this fashion, as it would ultimately mean smaller data sets returned from a remote call, especially with large recordsets. John's CFJsonReader, a custom data reader and an extended component of Ext's base DataReader, was able to translate ColdFusion's data returns by properly parsing the JSON return into Records of an Ext Store. It worked fairly well, with a few minor exceptions. it didn't handle the column aliasing you could do with any other Ext JS data reader (name:'development',mapping:'STRDEVELOPMENT') it didn't allow data type association with a value, as other Ext JS data readers (INTZIP is of type 'int', STRDEVELOPMENT is of type 'string', etc) So, it worked, but ultimately was limited. When I was writing Chapter 13, "Code for Reuse: Extending Ext JS", I really dove into extending existing Ext JS components. This helped me gain a better understanding of what John had done, when writing CFJsonReader. But, after really reviewing the code, I saw there was a better way of handling ColdFusion's JSON return. What it basically came down to was that John was extending Ext's base DataReader object, and then hand parsing almost the entire return. Looking at the above examples, you'll notice that Adobe's implementation is an array of arrays, rather than an array of objects. Ext JS already comes with an ArrayReader object, so I knew that by writing a custom data reader that extended it I would be able to get the desired results. Half an hour later, I had "built a better mousetrap" and we now have a Custom Data Reader for properly parsing ColdFusion's JSON return, without the previous limitations. /* * Ext JS Library 2.0 * Copyright(c) 2006-2007, Ext JS, LLC. * [email protected] * * http://extjs.com/license * ******************************************* * Steve 'Cutter' Blades (CutterBl) no.junkATcutterscrossingDOTcom * http://blog.cutterscrossing.com * * Inspired by the CFJsonReader, originally writtin by John Wilson (Daemach) * http://extjs.com/forum/showthread.php?t=21408&highlight=cfjsonreader * * This Custom Data Reader will take the JSON return of a ColdFusion * Query object, rather returned straight up, or via the ColdFusion * QueryForGrid() method. * * The CFQueryReader constructor takes two arguments * @meta : object containing single key/value pair for the 'id' of each record * @recordType : field mapping object * * The recordType object allows you to alias the returned ColdFusion column * name (which is always passed in upper case) to any 'name' you wish, as * well as assign a data type, which your ExtJS app will attempt to cast * whenever the value is referenced. * * ColdFusion's JSON return, for a ColdFusion Query object, will appear in the * following format: * * {"COLUMNS":["INTVENDORTYPEID","STRVENDORTYPE","INTEXPENSECATEGORIESID", * "STREXPENSECATEGORIES"],"DATA" :[[2,"Carpet Cleaning",1,"Cleaining"], * [1,"Cleaning Service",1,"Cleaining"]]} * * The ColdFusion JSON return on any query that is first passed through * ColdFusion's QueryForGrid() method will return the object in the * following format: * * {"TOTALROWCOUNT":3, "QUERY":{"COLUMNS":["MYIDFIELD","DATA1","DATA2"], * "DATA":[[1,"Bob","Smith"],[6,"Jim","Brown"]]}} * * The Ext.data.CFQueryReader is designed to accomodate either format * automatically. You would create your reader instance in much the same * way as the CFJsonReader was created: * * var myDataModel = [ * {name: 'myIdField', mapping: 'MYIDFIELD'}, * {name: 'data1', mapping: 'DATA1'}, * {name: 'data2', mapping: 'DATA2'} * ]; * * var myCFReader = new Ext.data.CFJsonReader({id:'myIdField'},myDataModel); * * Notice that the 'id' value mirrors the alias 'name' of the record's field. */ Ext.data.CFQueryReader = function(meta, recordType){ this.meta = meta || {}; Ext.data.CFQueryReader.superclass.constructor.call(this, meta, recordType || meta.fields); }; Ext.extend(Ext.data.CFQueryReader, Ext.data.ArrayReader, { read : function(response){ var json = response.responseText; var o = eval("("+json+")"); if(!o) { throw {message: "JsonReader.read: Json object not found"}; } if(o.TOTALROWCOUNT){ this.totalRowCount = o.TOTALROWCOUNT; } return this.readRecords(((o.QUERY)? o.QUERY : o)); }, readRecords : function(o){ var sid = this.meta ? this.meta.id : null; var recordType = this.recordType, fields = recordType.prototype.fields; var records = []; var root = o.DATA; // give sid an integer value that equates to it's mapping sid = fields.indexOfKey(sid); // re-assign the mappings to line up with the column position // in the returned json response for(var a = 0; a < o.COLUMNS.length; a++){ for(var b = 0; b < fields.length; b++){ if(fields.items[b].mapping == o.COLUMNS[a]){ fields.items[b].mapping = a; } } } for(var i = 0; i < root.length; i++){ var n = root[i]; var values = {}; var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null); for(var j = 0, jlen = fields.length; j < jlen; j++){ var f = fields.items[j]; var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j; var v = n[k] !== undefined ? n[k] : f.defaultValue; v = f.convert(v, n); values[f.name] = v; } var record = new recordType(values, id); record.json = n; records[records.length] = record; } if(!this.totalRowCount){ this.totalRowCount = records.length; } return { records : records, totalRecords : this.totalRowCount }; } }); So, this changes our examples for Chapter 12 just a little bit. First of all, we'll need to have the CFQueryReader included, in place of the CFJsonReader. You can change the script tags in the samples for Examples 3 and 4. ... <script language="javascript" type="text/javascript" src="/scripts/custom-ext/CFQueryReader.js"></script> ... Next, we'll change the scripts for these two examples. We'll remove our configuration references for CFJsonReader, and replace them with the updated configuration for the CFQueryReader. /* * Chapter 12 Example 3 * Data Store from custom reader * * Revised: SGB (Cutter): 12.17.08 * Replaced CFJsonReader with CFQueryReader */ // Save all processing until the // DOM is completely loaded Ext.onReady(function(){ var ourStore = new Ext.data.Store({ url:'Chapter12Example.cfc', baseParams:{ method: 'getFileInfoByPath', returnFormat: 'JSON', queryFormat: 'column', startPath: '/images/' }, reader: new Ext.data.CFQueryReader({ id: 'NAME', // This is supposed to match the 'mapping' fields:[ {name:'file_name',mapping:'NAME'}, {name:'file_size',mapping:'SIZE'}, {name:'type',mapping:'TYPE'}, {name:'lastmod',mapping:'DATELASTMODIFIED'}, {name:'file_attributes',mapping:'ATTRIBUTES'}, {name:'mode',mapping:'MODE'}, {name:'directory',mapping:'DIRECTORY'} ] }), fields: recordModel, listeners:{ beforeload:{ fn: function(store, options){ if (options.startPath && (options.startPath.length > 0)){ store.baseParams.startPath = options.startPath; } }, scope:this }, load: { fn: function(store,records,options){ console.log(records); } }, scope:this } }); ourStore.load(); }); /* * Chapter 12 Example 4 * Data Store from custom reader - Filtering * * Revised: SGB (Cutter): 12.17.08 * Replaced CFJsonReader with CFQueryReader */ // Simple function/object to 'clone' objects cloneConfig = function (config) { for (i in config) { if (typeof config[i] == 'object') { this[i] = new cloneConfig(config[i]); } else this[i] = config[i]; } } // Save all processing until the // DOM is completely loaded Ext.onReady(function(){ var initialBaseParams = { method: 'getDirectoryContents', returnFormat: 'JSON', queryFormat: 'column', startPath: '/testdocs/' }; var ourStore = new Ext.data.Store({ url:'Chapter12Example.cfc', baseParams: new cloneConfig(initialBaseParams), reader: new Ext.data.CFQueryReader({ id: 'NAME', // This is supposed to match the 'mapping' fields:[ {name:'file_name',mapping:'NAME'}, {name:'file_size',mapping:'SIZE'}, {name:'type',mapping:'TYPE'}, {name:'lastmod',mapping:'DATELASTMODIFIED'}, {name:'file_attributes',mapping:'ATTRIBUTES'}, {name:'mode',mapping:'MODE'}, {name:'directory',mapping:'DIRECTORY'} ] }), listeners:{ beforeload:{ fn: function(store, options){ for(var i in options){ if(options[i].length > 0){ store.baseParams[i] = options[i]; } } }, scope:this }, load: { fn: function(store, records, options){ console.log(records); }, scope: this }, update: { fn: function(store, record, operation){ switch (operation){ case Ext.record.EDIT: // Do something with the edited record break; case Ext.record.REJECT: // Do something with the rejected record break; case Ext.record.COMMIT: // Do something with the committed record break; } }, scope:this } } }); ourStore.load({recurse:true}); filterStoreByType = function (type){ ourStore.load({dirFilter:type}); } filterStoreByFileType = function (fileType){ ourStore.load({fileFilter:fileType}); } clearFilters = function (){ ourStore.baseParams = new cloneConfig(initialBaseParams); ourStore.load(); } }); Summary These very basic changes have no overall effect on our examples. They function exactly as they did before. The new Custom Data Reader loads the data, returned from ColdFusion, exactly as it should. Now, we can also work with these data stores in the same manor as we would with any other data store set up through Ext JS, having the ability to alias columns, define field data types, and more.
Read more
  • 0
  • 0
  • 2635