Tuesday, January 11, 2011

Local storage proxy with Sencha Touch

I am on my way to write my first app on sencha touch together with a friend. One of the abilities we want to have in our app is to allow the  user to save his personal settings on his device. In order to make it happen we had to teach ourselves how to use local storage proxy.

save my name

To achieve our goal we decided to build the simplest app -  get name -> save to file + display on screen + load value on the next app load.

the panels

Form panel - where we get the name and press the button for the update

Display panel - where we display the saved name

The code for the panels:

   1: Ext.setup({
   2:     icon: 'icon.png',
   3:     glossOnIcon: false,
   4:     tabletStartupScreen: 'tablet_startup.png',
   5:     phoneStartupScreen: 'phone_startup.png',
   6:     onReady: function () {
   7:  
   8:         // formpanel for the input
   9:         var formpanel = new Ext.form.FormPanel({
  10:             title: 'form',
  11:             //textfield for the name
  12:             items: [{
  13:                 xtype: 'textfield',
  14:                 label: 'name',
  15:                 name: 'name'
  16:             }],
  17:             //button for the handler (can be replaced by update listener for the text field)
  18:             dockedItems: [{
  19:                 dock: 'top',
  20:                 items: [{
  21:                     xtype: 'button',
  22:                     text: 'diplay',
  23:                     handler: function () {}
  24:                 }]
  25:             }]
  26:  
  27:         });
  28:         //panel for display
  29:         var panel = new PanelExtend({
  30:             title: 'display2'  
  31:          });
  32:         //the tab pannel definitions
  33:         new Ext.TabPanel({
  34:             fullscreen: true,
  35:             type: 'dark',
  36:             sortable: true,
  37:             items: [formpanel, panel]
  38:         });
  39:             
  40:     }
  41:  
  42: });
  43: // create a structure for the pannel so we can use it again if needed
  44: var PanelExtend = Ext.extend(Ext.Panel,{
  45:         title:'empty'
  46:         
  47: });
  48:  
  49: Ext.reg('mypanel',PanelExtend)    

In order to update the data in the second page we added a small handler to pannelExtand at the end of the code so the new declaration is:




   1: var PanelExtend = Ext.extend(Ext.Panel,{
   2:             title:'panel',
   3:             doit: function(name){
   4:                 this.update(name);
   5:             }
   6:     });


We also added the handler for the button on the form panel:



   1: var formpanel = new Ext.form.FormPanel({
   2:         title: 'form',
   3:         items: [{
   4:             xtype: 'textfield',
   5:             label: 'name',
   6:             name: 'name'
   7:         }],
   8:         dockedItems: [{
   9:             dock: 'top',
  10:             items: [{
  11:                 xtype: 'button',
  12:                 text: 'diplay',
  13:                 handler: function () {
  14:                     //get the value from the field
  15:                     var values = formpanel.getValues();
  16:  
  17:                     //call the update function
  18:                     panel.doit(values.name);
  19:                 }
  20:             }]
  21:         }]
  22:  
  23:     });

As you see in the code, in this stage all it does is to activate the handler of the display panel


defining the storage


In order to define the storage to be a local storage proxy we added the following code to the onready function of the Ext.setup:



   1: // the sructure for every row in the file
   2: var UserSettings = Ext.regModel('UserSettings', {
   3:     fields: ['id', 'name', 'value']
   4: });
   5: // the storage 
   6: var store = new Ext.data.Store({
   7:     proxy: new Ext.data.LocalStorageProxy({
   8:  
   9:         id: 'data',
  10:         //very important for the row update - basicaly, the row number is taken from the id field in the userSettings structure
  11:         proxy: {
  12:             idProperty: 'id'
  13:         }
  14:     }),
  15:     //uses the userSettings structure
  16:     model: 'UserSettings',
  17:     autoLoad: true,
  18:     autoSave: true
  19: });
  20: //sync with local file
  21: store.sync();
  22: //what to do when file is read, right now the only thing important to us us the scope
  23: store.read({
  24:     scope: this,
  25:     callback: function (records) {
  26:         //code to load the first record
  27:     }
  28: });


Most posts online show how to update a local storage file without taking care of the location of the data in the file. If you build a score record or just want to monitor the user data you do not need to have the idProperty definition but it is important if you want to update a certain row in the file. In our experiment and for the program we develop we wanted to be able to have one line for every variable in the storage, therefore we added the idProperty.


loading the data to the display pannel


Since we already loaded and synced the data we can now use our update function (pannel.doit), we added the following line as the last row of the onrReady function:



   1: panel.doit(store.getAt(0).data.value);

The getAt is pointing to the first entry of the file, knowing that it is the only entry. if you build a bigger file, it is advised to build a small function that builds the file structure( fior example: row 0 is name, row 1 is last name, etc…)


saving the data


in the handler of the button, we added the part that saves the data to the file:



   1: //read data
   2: store.read();
   3: //get the first item
   4: var item = store.getAt(0);
   5: //if not exists, build the first item (this area needs to be replaced with a function that construct a whole settings file
   6: if (!item) {
   7:     //constract item's structure
   8:     var item = new UserSettings({
   9:         id: 1, name: 'user name', value: values.name
  10:     });
  11:     //add new item to file
  12:     store.add(item);
  13:     //sync item
  14:     store.sync();
  15:  
  16: }
  17: //set value in item
  18: item.set('value', values.name);
  19: //sync items to file
  20: store.sync();

Saving the data can be done in two ways – if you want to add a row to a file every time you save all you need to do is to use store.add() and store.sync() but if you do not want to add rows every time you update you need to check if the row exists (add it if not) and then call item.set and store.sync() as it is shown in the code above.


The way we save the data in this example is by assigning a line for every property. this is not a must and for our software needs it will be replaced as a line for every user. All users data can be saved in the same row by changing the structure of the userSettings field. the only thing that I suggest to leave is the id field because it is the connection between the row number and the id of the field.


links


Using data package  - from the sencha touch website.


Offline Apps with HTML5: A case study in Solitaire – from the Ed Spencer blog, gave us the basics about storage.


sencha howto – this link was the biggest help to understand the subject of how to load the data, thanks, cipto.

Thursday, August 19, 2010

CCK compound fields for Quick-tabs module

In one of our projects we needed to let the user create his own tabbed data. in general, we wanted a simple and fast solution for user to be able to enter as much data as he wants in as much tabs as he wants.

Quick tabs

image

Link : http://drupal.org/project/quicktabs

A very simple solution for fast creation of tabs. I am not going to describe the module but when you go deeper to the modules files you will find a very structured and easy way to design and create  tabs layout. In the documentation you can find the instructions of how to add the tabs, we needed a programatic way that allows us to insert any kind of code in the tab so we chose this basic code:

 

$tabs['fourth'] = array(
  'title' => t('Four'),
  'type' => 'freetext',
  'text' => 'Hello World',
);
$quicktabs['qtid'] = 'any-unique-id';
$quicktabs['tabs'] = $tabs;
$quicktabs['style'] = 'Excel';
$quicktabs['ajax'] = FALSE;
print theme('quicktabs', $quicktabs);
?>

Knowlage resources


There are three knowledge resources which we based on. the first is coming from the excellent bolg -



our goals



  • User should be able to enter the title and the body of the tab.

  • User should have the ability to enter multiple values to the field

  • Editing the body area should be using a wysiwyg editor

  • Tabs should be easy to use and embeded in the module code.

  • Tabs should be easy to CSS.

Building the module


Based on Zardoz’s example we used two kinds of ‘sub-fields’ – ‘textfield’ for the title and ‘textarea’ for the body.


The module itself has three main files:



  • module_name.info – the information file for Drupal this is where you define dependencies.

  • module_name.install – install / uninstall instructions for Drupal and CCK.

  • module_name.module – the actual module, I am not going to describe the whole structure of the file – the three resources are doing a good enough job.

Basically the process was easy, we switched the names, edited the columns and fields and added the right dependencies in the info files (you have to include the quicktabs module…) 





problems we encountered


CCK multi-value building structure that is defined by the formatter in the imagetabsfield_field_formatter_info() function is set to CONTENT_HANDLE_CORE or CONTENT_HANDLE_MODULE, we still didn’t find a way to create the multiple-value processing by ourselves and for now, theme_tabsfield_formatter_default() function just transfers the raw data.


for now, because our nodes have their own node-node_name.tpl.php file we are processing the tabs in the theme process instead of doing it in the data creation process.


wysiwyg editor – separate module that will be installed in time to come – not a part of the field. one problem that can come when installing – not all editors know how to work with multiple value fields.


TO-DO



  • Move field processing to the save data process –this will make the field a standalone with no coding involved when installed.

  • WYSIWYG editor.

  • Free text handeling for better presentations

  • Ah yes ! css!

What you need in order to get credit card payment working with Ubercart

Issue: Get credit card payments working for Ubercart in DRUPAL

Solution: You need to do the following things:

  1. Buy and install an SSL Certificate (possible sites Verison, GeoTrust, and RapidSSL)
  2. Download and Install Secure Pages module and Secure Pages Hijack Prevention module
  3. Set a folder outside of the DRUPAL root directory that the Ubercart module is allowed to access so it can create the keyfile to encrypt credit card data
If you do steps 1 and 2 you have met the current security standards (PCI DSS). And doing step three satisfies Ubercart's remaining requirement.

You will also need to set the payment gateway for credit cards. As of 8/19/10 I am still looking into what those settings need to be.

- Daniel Casey

Wednesday, August 18, 2010

Granting new roles to DRUPAL users using Ubercart

So you want to great new roles to users using Ubercart...

Welcome to the right place.

Step 1
First you need to make sure you have create the DRUPAL roles you will want to assign.

Step 2
Then you need to make sure you have installed Ubercart module and turned on the following modules in "Module" list:

  • Ubercart - Core (all)
  • Ubercart - Core - Optional (Catalog, Roles)
Step 3
Now you need to create the categories (or for Ubercart, "Classes") of products you will want to create. I knew I had two main types of roles with two sub roles in each main type. So I went to "Store Administration >>> Products >>> Manage Classes" and added two classes for my two main role types.

Doing this creates two new content types within DRUPAL.

Step 4
However before I can create my instances of these new content types I need to add category terms to the Ubercart Catalog, which is ultimately responsible for the grouping and presentation of my products on my website. To do this I need to go to "Content management >>> Taxonomy >>> List" and select the "Add Terms" for the Catalog taxonomy list.

I added two terms, Role Type A and Role Type B.

Step 5
Now you need to create the actual products that a customer will buy and that will grant them a new role.

To create the content go to "Content management >>> Create Content" and select the appropriate content node type. Then fill in the required information, make sure to select the appropriate catalog category and hit save.

You now have a product that you can play with.

Step 6
Click on "Content management >>> Content" to view a list of all your site content. Find your newly created content and select "edit". Find and select the "features" menu item, make sure that "Add new feature" is set to "Role assignment" and hit the "Add" button.

Chose the "SKU" value that corresponds to this product, select the appropriate role that you want granted once a customer purchases this product, make any other setting adjustments and then hit "save feature"

Step 7
Now you should be able to go to your website's catalog, select the product that will grant the role, purchase the product, and upon successful completion of payment you will:

  • Receive an e-mail from the website to your user account e-mail address notifying you of your role addition
  • Be given a new role for the website

Note: it is important to make sure that user can see the catalog to buy the role. Therefore you need to make sure the proper permissions are in place for "view catalog".

- Daniel Casey



Possible SEO modules for SEOing in DRUPAL

While searching through DRUPAL's modules I came across these that could be the bases for SEO functionality. Still need to review them:


It was interesting to see that there are some monthly pay services that offer automated SEO support.

- Daniel Casey

Doing something after a customer has bought a product in Ubercart

To do something in DRUPAL after a customer has bought a product in Ubercart you can use Ubercart's Conditional Action feature.

We were trying to figure out how to:
  • update a interger field (counter) in a node
  • call a print/email function
  • send a user to a certain DRUPAL page
once they bought a product through Ubercart. However we needed to be sure that the user actually bought and paid for the product and that the payment was successful.

The simplest way for us was to create a predicate in Ubercart's Conditional Actions where the tigger was "User Completes Checkout" with the following Conditions:
  • order balance = $0.00
  • AND product type = (we were able to set the appropriate product type we wanted given the resulting action we wanted to take place)
This trigger and conditions combination allows us to then call an action "Run Custom PHP Code" where we were able to write the custom code we wanted to occur after a user has successfully purchased a specific product.

With the right conditions and the custom PHP code we were home.

- Daniel Casey
dcasey@data-that.com
www.data-that.com

Testing PayPal Functionality of Your Ecommerce Site with PayPal Sandbox

If you are developing an e-commerce site you can use PayPal's Sandbox to your site's PayPal functionality.

We were developing a DRUPAL site and using Ubercart. Took about 10 minutes to:

  • create the PayPal sandbox account (do not use the e-mail for existing PayPal account);
  • set up a buyer and a seller;
  • and copy the PayPal Sandbox API credentials into the Ubercart PayPal configuration settings
Then we were able to test the PayPal functionality of our site. It was amazing.


- Daniel Casey