Skip to content

Jahia 6.5 vs Liferay 6.0 Part 2: Extensibility

February 29, 2012

This is part two of my comparison of Jahia and Liferay. As I mention in my post “Jahia 6.5 vs. Liferay 6.0 Part 1: Content Integration”, both products are excellent solutions for the implementation of Enterprise Web Applications. This post focuses on the Extensibility Model and the different ways that each product handles the need to implement custom functionality.

Extensibility Model

Both products present similar ways to add/extend functionality, but there are some differences.

Add/Extend Portable Functionality

Portlets are supported by and can be used in both products. The development effort in both cases is almost the same, but both systems provide to developers different tools for development. Liferay has the “Plugins SDK” tool in order to create portlet projects. In a similar way, Jahia has a maven archetype that can be used for this same purpose.

Jahia also can be extended via modules that are considered plugins. The advantage of modules over portlets is that they don’t need a Portal Sever and they are integrated directly into Jahia’s core gaining direct access to Jahia features. This means that developers using modules, apart from extending Jahia functionality, can add functionality to the default Jahia modules. Liferay doesn’t have this type of extensibility so I consider it an advantage of Jahia over Liferay.

Extend/Modify Core
Liferay has a very interesting method to overwrite files, beans, Struts Actions and properties in the core; it uses Hooks and Ext Plugins. This feature is based on the configuration of xml files that contain information about which files are going to be replaced. This feature does not exist on Jahia as those modules just can add new files but cannot replace core files.

I think that both products are excellent solutions and can work for any type of Enterprise Web Application. There are some differences, like in the Extensibility Model examples above, but in the end, both products can accomplish the same objectives (maintainability, usability, and portability). Also, the development time, effort and skills are similar because each product offers powerful tools, like Jahia Maven archetype and Liferay SDK to generate extensibility projects

Summary of Extensibility:

Extensibility
Jahia Liferay Comments Winner
Add/Extend Portable Functionality Modules

And Portlets

Portlets Jahia Support Portlets but Liferay doesn’t support modules Jahia
Extend/Modify the core Modules Hooks and Ext Plugin Jahia doesn’t have the concept of file mapping replacements Liferay

Original Post

Improving Mobile User Experience with HTML5

February 29, 2012

Nowadays, people are constantly browsing the web via their mobile devices, there are 5.3 billion mobile subscribers in the world and the percentage of smart phones is growing exponentially. There are a few easy techniques using HTML5 that you can apply to your mobile apps and webpages that will improve user experience significantly.

Inputs

You’ve probably filled out a registration form from your phone, and it’s not the nicest thing, screen needs resizing between reading labels and entering text and you also need to switch back and forward from the text keyboard, to the numeric and symbols keyboard.

Luckily HTML5 has several enhancements for input fields. And even if many browsers don’t have full support for all these features, you can still add a lot of them without problems. Only browsers that support them will notice the difference, and older browsers will behave like they always do (HORRIBLE!).

HTML5 Input Techniques

Here’s a list of all the input types available, you will notice that there are a lot more than most people are aware of:

  • button
  • checkbox
  • color
  • date
  • datetime
  • datetime-local
  • email
  • file
  • hidden
  • image
  • month
  • number
  • password
  • radio
  • range
  • reset
  • search
  • submit
  • tel
  • text
  • time
  • url
  • week

Though it’s nice to know there are so many options, keep in mind that not every one of them is going to work for your site. The most common and also the most supported new HTML5 elements are: email, number, tel, and URL. I’m sure you’re already thinking on great uses for those!

Another great thing is that browser validation is already provided on some browsers, and my personal opinion is that all browsers will have that sooner or later, so we can see a future without JavaScript validations.

But let’s get back to mobile devices and see the advantage of using the proper type of HTML5 input.

If your register form asks for an age or phone number, you’ll want to make sure that the user only enters numbers. To do this, use the input type = “number”. When you do this, the keyboard will automatically change to a numeric keyboard, and look something like this:

numPretty cool from a user’s point of view, right? Now let’s go through some pretty useful attributes that you can add to your input tags.

Input Attributes

  • autocomplete
  • autofocus
  • form
  • formaction
  • formenctype
  • formmethod
  • formnovalidate
  • formtarget
  • height
  • list
  • max
  • min
  • multiple
  • pattern
  • placeholder
  • required
  • step
  • width

These are all the “new” attributes according to http://www.w3schools.com. My favorite one is definitely the “placeholder” attribute. It puts a descriptive text on your input that clears on focus and shows on blur if you haven’t typed anything. Classical behavior for which you needed to use JavaScript.

numOther great attribute for mobile devices is the “autocapitalize”. You can set it to “on” or “off” and it works perfectly for fields that should not contain capital letters, like URLs or for fields that should always begin with a capital letter, like names.

As you know, smart phones have dictionaries that correct spelling. The “autocorrect” attribute allows you to turn “on” or “off” this feature, to avoid annoying messages and underlines when you don’t want them.

Anchors

Lastly, a great thing to have on all your “contact us” sections: Specialized Anchors.

Most developers know how to use “mailto:your@email.com”. You place that code inside your href attribute on any anchor tag and when a user clicks it, it opens your default email client and sends an email to the specified recipient.

Wouldn’t it be great if there was something like that for your mobile site that could place a call or send an SMS? There is and it’s as simple as the mailto option! Just place it on your anchor tab.

Here’s an example of each one:

	 <a href="tel:1-555-123-1234">CALL PHONE</a> <a href="sms:1-555-666-7788">SEND SMS</a> 

Hope you enjoyed!!

Original post.

Jahia 6.5 vs Liferay 6.0 Part 1: Content Integration

February 21, 2012

Jahia and Liferay are excellent solutions for the implementation of Enterprise Web Applications. This comparative analysis is focused on the Content Integration Model and Extensibility Model. It covers how the two systems handle the need to consume content from various internal and external sources like integration with external services, integration with different DBMS, consuming external RSS, and communication between components.

Content Integration

Architecture & Web Services Integration

Jahia and Liferay are products with a robust architecture that use the latest Java framework to accomplish their goals. Both products use the Apache Jackrabbit for content storage and can also be configured to access other types of repositories via the JCR API. One architecture difference is that Liferay has an architecture based on SOAP and Jahia uses the REST API, but using both ways we can integrate both products to external services.

Supported Databases
Both products support almost all the state-of-art engines, but there is a big difference on the community versions. Liferay EE and CE support Microsoft SQL Server and Oracle, but Jahia only supports those DBMS in the Enterprise version.

RSS Integration
The rendering of RSS feeds is supported on both products and both have ready to go portlets/modules for rendering. Jahia has the “Display External RSS” module and Liferay has the RSS Portlet. Jahia’s “Display External RSS” uses the RSS URL and number of entries, in the same way Liferay’s RSS Portlet uses them.

Communication between components
Communication to different databases is supported by both products. Jahia and Liferay can connect to different databases adding different sources in the context.xml file. The sharing of information between components in Liferay can be done via the Inter-Portlets Communication API. The sharing of information between modules in Jahia seems to be more integrated, because Jahia modules can communicate each other by simply using the context of a session.

Quick comparison table:

Content Integration
Jahia Liferay Comments Winner
Repository Integration Apache Jackrabbit Apache Jackrabbit Both can be integrated with different types of repositories Tie
Supported DBMS SQL Server, Oracle, My SQL, etc. SQL Server, Oracle, My SQL, etc. SQL Server and Oracle are supported only on the Jahia EE Liferay
Web Services Integration REST SOAP REST vs SOAP? Tie
RSS Integration Supported Supported Supported on both products Tie
Data Sharing Via sessions IPC API Simpler Data Sharing Jahia

Original Post

JavaScript Navigation using Hash Change #

February 15, 2012

Modern web applications often load data or changes in the document object model (DOM) via AJAX without leaving the original page, to create a desktop-like experience. To do that we can take advantage of the URL hash value and the benefits it give us, including less bandwidth used by the client, faster response time and eventually a more interactive application feeling. By inserting a hash character into the URL we can pick up the string following it and use it as a value in AJAX requests. We can also create a listener event to check for a hash being added to a URL and use it to activate a custom JavaScript.

The hash symbol can enable backward and forward browser navigation support in a web page, without the need to reload the entire page. Using JavaScript and the “#” we can enable the browser history for Ajax calls and for DOM updates.

The content after the “#”in the URL is not sent to the server, since it has a hash in the URL. The content itself is accessible using JavaScript, using the variable window.location.hash. It has two big advantages when used with AJAX: first, it enables the browser history even if the web page is based on AJAX (this depends on the implementation, of course). Second, you can “share” or “bookmark” the link, so even if it is an Ajax call, it can be launched when you enter the page with the same link and with the same hash.

With JavaScript functions running and updating our DOM, we can change the hash with each JavaScript function. This means when the user hits the “back” button, the hash goes to the previous hash, and now since we have a listener, we can reload/re-render the previous content.

The most common form that uses the hash, and that many already know is to make a scroll down on the screen to the HTML tag with the ID assigned. For example:

If the hash in the URL indicates, for example: #end (http://www.domain.com/page.html#end), the browser will scroll the screen to the item that has that ID.

Here, I will show you how to build a page that shows a set of images and when an image is clicked, it will show an overlay. With every event, the URL is going to change.

 

 

The Code

First, we would need to include the jQuery library:

1
2
3
4
5
6
7
8
9
10
11
<!DOCTYPE html>
<html lang="es">
<head>
 
<script src="js/jquery.min.js" type="text/javascript"></script>
 
</head>
<body>
 
</body>
</html>

 

After that we will need to add the HTML structure:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<!DOCTYPE html>
<html lang="es">
<head>
 
<script src="js/jquery.min.js" type="text/javascript"></script>
 
</head>
<body>
 <div style="width: 500px; margin: 0pt auto;">
 <div>
 <h1>click on an image</h1>
 </div>
 <div id="demos" class="content">
 <div class="pics" id="slideshow">
 <a href="#overlay01" onclick="change_hash('overlay01'); return false; " ><img  alt="" src="Images/logo001.png" /></a>
 <a href="#overlay02" onclick="change_hash('overlay02'); return false; " ><img  alt="" src="Images/logo002.png" /></a>
 <a href="#overlay03" onclick="change_hash('overlay03'); return false; " ><img  alt="" src="Images/logo003.png" /></a>
 </div>
 </div>
 </div>
<div class="overlays" style="width: 500px; margin: 0pt auto;">
 <div class="item" id="overlay01" >
 <img alt="" src="Images/logo001.png" />
 <p class="text">
 click in the image to close overlay
 </p>
 </div>
 <div class="item" id="overlay02" >
 <img alt="" src="Images/logo002.png" />
 <p class="text">
 click in the image to close overlay
 </p>
 </div>
 <div class="item" id="overlay03" >
 <img alt="" src="Images/logo003.png" />
 <p class="text">
 click in the image to close overlay
 </p>
 </div>
 
</div>
</body>
</html>

 

In the body structure we have a div with the class “pics” which holds a set of images and a div with the class “overlays” which holds the overlay elements that are going to be shown when an image is clicked

Now we have to define the JavaScript code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
<script type="text/javascript">
$(function() {
//set up hash detection
$(window).bind( 'hashchange', function(e) {
 var hash = '';
 var hashlenght = 0;
 hashwith = '';
 if (location.hash == ''){
 location.hash = '#pages=index';
 document.title = 'Work Index';
 }
 hash = location.hash;
 hashlenght = hash.length;
 hashwith = '#'+hash.substring(7, hashlenght);
 hash = location.hash.substring(7, hashlenght);
 if(hashwith != "#"+'index'){
 show_overlay(hash);
 document.title = 'Work '+hash;
 }
 else{
 close_overlay();
 
 }
 
 });
 $(window).trigger( 'hashchange' );
 $(".overlays .item img").each(function(){
 $(this).click(function(){
 close_overlay();
 });
 });
});
function close_overlay(){
 $(".overlays .item").each(function(){
 if($(this).css('display') == 'block'){$(this).hide();}
 });
 location.hash = '#pages=index';
}
//function to show overlay
function show_overlay(aux){
 $("#"+aux).show();
}
//function to change hash
function change_hash(aux){
 location.hash = '#pages='+aux;
 document.title = 'Work '+aux;
}
</script>

 

The JavaScript code will:

  • Bind a handler to the window.onhashchange event. Each time the hash changes is compared to the hash string to check if the page is in its original state or if an overlay was clicked
  • The code receives the hash string to check if an overlay was clicked in the page, to execute a “show_overlay()” or “close_overlay()” function.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//set up hash detection
$(window).bind( 'hashchange', function(e) {
 var hash = '';
 var hashlenght = 0;
 hashwith = '';
 if (location.hash == ''){
 location.hash = '#pages=index';
 document.title = 'Work Index';
 }
 
 hash = location.hash;
 hashlenght = hash.length;
 hashwith = '#'+hash.substring(7, hashlenght);
 hash = location.hash.substring(7, hashlenght);
 if(hashwith != "#"+'index'){
 show_overlay(hash);
 document.title = 'Work '+hash;
 }
 else{
 close_overlay();
 
 }
 
 });
  • Trigger all bound window.onhashchange event handlers
1
$(window).trigger( 'hashchange' );
  • Assign a function to close an opened overlay when the image in the overlay is clicked
1
2
3
4
5
$(".overlays .item img").each(function(){
 $(this).click(function(){
 close_overlay();
 });
});
  • Function to close an opened overlay
1
2
3
4
5
6
function close_overlay(){
 $(".overlays .item").each(function(){
 if($(this).css('display') == 'block'){$(this).hide();}
 });
 location.hash = '#pages=index';
}

 

  • Function to show an overlay
1
2
3
4
//function to show overlay
function show_overlay(aux){
 $("#"+aux).show();
}
  • Function to change the hash
1
2
3
4
5
//function to change hash
function change_hash(aux){
 location.hash = '#pages='+aux;
 document.title = 'Work '+aux;
}

 

Check out the demo here

Although the hash can have a powerful uses, it should not be the replacement of basic friendly URLs and permalinks that we all know. Google does not take into account the hash when indexing a page, the following pages will appear the same to Google and other search engines, and will not help with indexing and SEO:

http://www.example.com/seccion/#Com45

http://www.example.com/seccion/#445532q

http://www.example.com/seccion/#file.html

Original post.

Jahia 6.5 Integration of Jahia Actions and DataTables

February 7, 2012

A few months ago, I wrote a post about a Jahia custom module I created called DB SQL Display. After receiving feedback about the module, I made an improvement to it that uses Jahia REST Actions and the jquery plugin DataTables.

Note: This module was tested in a Jahia 6.5.1 installation.

The architecture of this module has been built in order to show how developers can use DataTables to render results returned by AJAX calls to Jahia Actions. Datatables is a jQuery plugin that is in charge of the display of data as tables. This jQuery plugin can be configured according to your needs, like including paging in the data set. The following is a diagram that shows how this module works:

 

The complete source code of this module can be found here, and following is an image that shows how this module looks like in the ACME Site.

 

The steps to implement this are:

 

  1. Generate a Jahia module project using the Jahia archetype. Go to this page for more information http://www.jahia.com/cms/home/community/documentation/templating-and-integration-guide/part-2-modules/module-creation.html.
  2. Go to the file /src/main/webapp/META-INF/definitions.cnd and create the definitions for your module.  (Please check the source code as an example)
  3. Go to the file /src/main/webapp/META-INF/spring/SQLDisplay.xml and here you need to define the Action for your module for example:
    1
    2
    3
    4
    5
    6
    7
        <!--?xml version="1.0" encoding="UTF-8"?-->
    <bean class="org.jahia.modules.dbsqlDisplay.actions.DBSqlPaging">
    <property value="sqlPaging" name="name">
    </property>
    </bean>
    </beans>
  4. Create the DBSqlPaging Action in the folder /src/main/java/org/jahia/modules/dbsqlDisplay/actions/DBSqlPaging.java.
    Notice that the example provided of this class returns an org.json.JSONObject in the doExecute method and also the method isRequiredAuthenticatedUser() has been overwrote to return false in order to permit guest calls to this Action.
  5. Now is time to integrate the Datatables plugin.
    1. In the folder /src/main/webapp/javascript/ add the Datatables javascript file.
    2. In the folder /src/main/webapp/css/ add all the necessary styles sheets that are going to be used for your module. In my case I am using the same style sheet provided in http://datatables.net/release-datatables/media/css/demo_table.css with minor changes, like use other paging arrows.
  6. Create the jsp file that is going to be used to render the module. The jsp needs to be created in src\main\webapp\jnt_DBSqlDisplay\html\DBSqlDisplay.jsp.
    1. In your jsp please add references to your style sheet and javascript files.
    2. Create a table and add an “id” property to it.
    3. Bellow the table, add the initializer datatables code like:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    $(document).ready(function() {
                var oTable = $('#dbSqlTable').dataTable( {
                "bFilter": false,
                "bSort": false,
                "bSearchable": false,
                "bLengthChange": false,
                "iDisplayLength": ${currentNode.properties['pageSize'].string},
                "bProcessing": true,
                "bServerSide": true,
                "sAjaxSource": $('#redirectPath').val()
                } );
            } );

    Note: The above example is a way to initialize Datatables when you want to use a simple table with only page arrows for the paging. The iDisplayLenght property means the page size. The page size comes from a property of the Jahia module. The sAjaxSource is the path of the Ajax Action. The value of the path of the action is given by the expression:

    1
    <input value="<c:url value='${currentNode.path}.sqlPaging.do' context='${url.base}'/>?maxSize=${currentNode.properties['maxRows'].string}" name="redirectPath" id="redirectPath" type="hidden">

    .

  7. To this module I add localization to render the results. For that reason the jsp sample contains jsp calls to the properties files as:
    1
    2
      <fmt:message key="jnt_DBSqlDisplay.executeLabel">
    </fmt:message>

    Where jnt_DBSqlDisplay.executeLabel is a key defined in the file src\main\webapp\resources\SQLDisplay.properties as the default language and src\main\webapp\resources\SQLDisplay_fr.properties for French.

Using these guidelines, you’ll be able to integrate with Datatables and Jahia Actions using JSON. Notice that Datatables plugin support multiple table display configurations, like add numbered paging, search, column sorting, etc. In order to enable this, remember to implement the backed functionality to support those actions.

Original Post.

SaaS WCM TCO Comparisons are Premature

January 31, 2012
tags: , ,

I have read several posts recently about the merits of SaaS WCM and the reduction in Total Cost of Ownership (TCO) that they bring.  I’m not going to bother linking to them here because, to me, they read like marketing schlock from some SaaS WCM vendor.  I think any TCO comparisons between SaaS and traditional WCM deployments are premature, especially in the enterprise market.

I say this for two reasons: first, there are very few WCM vendors with SaaS offerings, and none in the enterprise market, so any comparisons are likely to be apples-to-oranges.  Second, just because SaaS could theoretically create efficiencies and opportunities for cost savings, it is too soon to know if this shift will actually result in TCO savings for WCM customers.

As Irina Guseva discussed in her recent post (http://www.realstorygroup.com/Blog/2256-No-liftoff-for-SaaS-Web-CMS.html), the SaaS WCM market hasn’t yet achieved “liftoff”.  The biggest players currently in the space are entry-tier, mid-market or niche vendors such as WordPress, Clickability, CrownPeak, and OmniUpdate.  TCO comparisons between these vendors and other traditional competitors within the lower and mid-markets might be valid, but claims that these SaaS offerings provide TCO savings for enterprise applications don’t make sense.  These are different markets with different requirements and products that have different capabilities.

It doesn’t make sense to compare the cost of my beloved stovetop espresso maker (roughly $20) with a Franke Sinfonia 1-Step Superauto ($22K).  They both make espresso, but have very different capabilities and are intended for very different markets.  The difference between mid-market and enterprise WCM solutions might be less striking, but I suspect very few enterprise WCM clients would consider one of the today’s SaaS WCM solutions to be a suitable alternative.

Even when the enterprise WCM vendors do enter the SaaS market, it has yet to be seen how that will affect TCO for customers.  These services are not priced based on what they cost to provide.  They are priced based on what customers are willing to pay – and that willingness is shaped by perceived value, pricing of suitable alternatives, and budget constraints.  If a SaaS solution creates efficiencies for a client (e.g. reduced IT staffing, hardware purchases, etc.), it increases the perceived value and frees up IT budget dollars, thus justifying an increased pricetag.   It is entirely possible that vendors will price their services to capture those saved budget dollars.  SaaS might not result in TCO savings; instead, it might yield higher margins for the enterprise vendors.

One example of this phenomenon is server hosting.  It is actually more expensive to host a single virtual server for a year on Amazon EC2 than hosting a dedicated server at Rackspace, even though the cost for Amazon to provide this service should be lower.  Amazon EC2 adds value to customers by allowing them to quickly spin-up and tear-down servers and use them for short periods of time, and that value translates into higher TCO for clients if they are doing long-term deployments.

Of course the hope is that once enough enterprise WCM vendors enter the SaaS market, the competition will help drive down prices closer to the true cost of the vendors to provide the service.  I have very little faith that this will actually happen – especially in the enterprise market.  The traditional WCM enterprise market is full of vendors now, but they are not exactly engaging in a race to the bottom on pricing.  The bottom line is that we still have to wait to see how SaaS WCM offerings will affect TCO in the enterprise market.

Original post.

Using Sitecore WebControl to Customize Google Calendar Reminder Button

January 25, 2012

Oshyn WebControls Sitecore Google CalendarWebControls allow the creation of reusable atomic components that fits well with the needs of semi-static HTML components. For this example of how to use WebControls in semi-static HTML components, we have created a Google Calendar Reminder button that can easily be reused/expanded. The code is a basic Sitecore WebControl with some custom parameters for setting the start/end times, location, details, title and item of the event.

Here is the code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Sitecore.Web.UI;
using System.ComponentModel;
using System.Collections.Specialized;
using Sitecore.Data.Items;
using Sitecore.Data.Fields;
using Sitecore.Diagnostics;
using Sitecore.Data;
namespace Oshyn.Sitecore.WebControls
{
    public class GoogleCalendarReminder : WebControl
    {
        private readonly string googleCalendarReminderHtml = "<a href="http://www.google.com/calendar/event%7B0%7D"><img alt="" src="%7B1%7D"></a>";
        private readonly string googleCalendarButton = "http://www.google.com/calendar/images/ext/gc_button2.gif";
        private string startDateTime = string.Empty;
        private string endDateTime = string.Empty;
        private string title = string.Empty;
        private string location = string.Empty;
        private string details = string.Empty;
        private string itemId = string.Empty;
        [Category("Method"), Description("Start DateTime for the Event")]
        public String StartDateTime
        {
            get { return startDateTime; }
            set { startDateTime = value; }
        }
        [Category("Method"), Description("End DateTime for the Event")]
        public String EndDateTime
        {
            get { return endDateTime; }
            set { endDateTime = value; }
        }
        [Category("Method"), Description("Title for the Event")]
        public String Title
        {
            get { return title; }
            set { title = value; }
        }
        [Category("Method"), Description("Location for the Event")]
        public String Location
        {
            get { return location; }
            set { location = value; }
        }
        [Category("Method"), Description("Details of the Event")]
        public String Details
        {
            get { return details; }
            set { details = value; }
        }
        [Category("Method"), Description("Item Id of the Event")]
        public String ItemId
        {
            get { return itemId; }
            set { itemId = value; }
        }
        protected override void DoRender(System.Web.UI.HtmlTextWriter output)
        {
            NameValueCollection googleCalendarParams = new NameValueCollection();
            if (string.IsNullOrEmpty(startDateTime)) //required value
                return;
            if (string.IsNullOrEmpty(endDateTime)) //required value
                return;
            if (string.IsNullOrEmpty(title)) //required value
                return;
            try
            {
                Item cEvent = GetItem();
                if (!string.IsNullOrEmpty(itemId) && global::Sitecore.Data.ID.IsID(itemId))
                    cEvent = Sitecore.Context.Database.GetItem(new ID(itemId));
                DateField start = cEvent.Fields[startDateTime];
                DateField end = cEvent.Fields[endDateTime];
                string gLocation = cEvent[location];
                string gDetails = cEvent[details];
                googleCalendarParams.Add("dates", string.Format("{0:yyyyMMdd'T'HHmmss'Z'}/{1:yyyyMMdd'T'HHmmss'Z'}", start.DateTime.ToUniversalTime(), end.DateTime.ToUniversalTime()));
                googleCalendarParams.Add("action", "TEMPLATE");
                googleCalendarParams.Add("text", HttpUtility.HtmlEncode(cEvent[title]));
                googleCalendarParams.Add("sprop", Context.Request.Url.Host);
                if (!string.IsNullOrEmpty(gDetails))
                    googleCalendarParams.Add("details", HttpUtility.HtmlEncode(gDetails));
                if (!string.IsNullOrEmpty(gLocation))
                    googleCalendarParams.Add("location", HttpUtility.HtmlEncode(gLocation));
                var response = string.Format(googleCalendarReminderHtml, ToQueryString(googleCalendarParams), googleCalendarButton);
                output.Write(response);
            }
            catch (Exception ex)
            {
                Log.Error("Error on Google Calendar Reminder, method: DoRemder", ex, this);
            }
        }
        private string ToQueryString(NameValueCollection nvc)
        {
            return "?" + string.Join("&", Array.ConvertAll(nvc.AllKeys, key => string.Format("{0}={1}", HttpUtility.UrlEncode(key), HttpUtility.UrlEncode(nvc[key]))));
        }
    }
}

To use this code, add the registration section in your aspx/ascx file and then set the control with the necessary parameters for it to display the button. Once that is done, it will look something like this:

1
2
3
4
<%@ register tagprefix="gcal" namespace="Oshyn.Sitecore.WebControls" assembly="Oshyn.Sitecore">
        <gcal:googlecalendarreminder details="Description" location="Location" title="Title" enddatetime="End Date" startdatetime="Start Date" itemid="{2A37207A-E546-4016-9ED5-1250B199C6DC}" runat="server">
</gcal:googlecalendarreminder>

For simple, semi-static HTML integrations, it is a good idea to use WebControls which give you the ability to be reuse them in multiple locations. To check out how to build more controls, check out these links:

- Sitecore Introduction (3 parts serie): http://sdn.sitecore.net/Articles/Web%20Controls/Building%20Web%20Controls%20-%20Part%201.aspx

- Stackoverflow : http://stackoverflow.com/questions/7070006/where-can-sitecore-webcontrol-examples-be-found

- Google Calendar Reminder Button: http://www.google.com/googlecalendar/event_publisher_guide_detail.html

Websites on Smartphones, iPad & Tablets – Using Touch Events for Image Scrolling on your site

January 17, 2012
Oshyn Gesture Events: PinchA requirement for every website is an easy and intuitive way for users to browse and navigate around a website. This requirement is also true when the user navigates in a mobile device, like a smart phone or tablet. Features like using a finger to swipe the content or using multi-touch gestures to zoom or rotate the content should be available on your site. These gestures can be modified to perform any different actions in a website.In this article we will overwrite the default behavior for the pinch gesture to execute a “go back” function in an image gallery.

Gesture Events

Gesture events are events that are triggered during a multi-touch sequence, typically pinching and rotating gestures. Gesture events contain scaling and rotation information, allowing gestures to be combined.

Apple supports gestures that are multi-touch events such as “pinching” and “rotating”:

  • gesturestart - triggered when initiating a multi-touch event
  • gesturechange - triggered when multiple touches move
  • gestureend - triggered when a multi-touch event ends

The event object for gesture events looks very different. It contains scale and rotation values and no touch objects. The event object has the properties:

  • event.scale – a value of 1 when there’s no scaling, less than 1 when zooming out (such as making our div smaller), and greater than 1 when zooming in.
  • event.rotate- the rotation angle in degrees.

Note: The default behavior of Safari on iOS can interfere with your application’s custom multi-touch and gesture input. You can disable the default browser behavior by sending the preventDefault message to the event object.

For more information on how to do this, visit this link

Example:

1
2
3
4
document.addEventListener('gesturechange', function(event) {
   event.preventDefault();
   console.log("Scale: " + event.scale + ", Rotation: " + event.rotation);
 }, false);

Event Table

GESTURESTART GESTUREMOVE GESTUREEND
Android n n n
iPhone y y y
has event.touches n n n
(iPhone) has event.scale and event.rotation y y y
(iPhone) touch in event.touches - - -
(iPhone) touch in event.changedTouches - - -

The code

To implement these functions we are going to use jQuery

First, we would need to include the jQuery library:

1
2
3
4
5
6
7
8
9
10
11
<!DOCTYPE html>
<html lang="es">
<head>
 
<script src="js/jquery.min.js" type="text/javascript"></script>
 
</head>
<body>
 
</body>
</html>

After that we will need to add the HTML structure:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<!DOCTYPE html>
<html lang="es">
<head>
 
<script src="js/jquery.min.js" type="text/javascript"></script>
 
</head>
<body>
<div style="width: 500px; margin: 0pt auto;">
        <div>
             <h1>click on an image</h1>
        </div>
        <div id="demos" class="content">
        <div class="pics" id="slideshow">
 
        <a href="#overlay01" ><img  alt="" src="Images/logo001.png" /></a>
        <a href="#overlay02" ><img  alt="" src="Images/logo002.png" /></a>
        <a href="#overlay03" ><img  alt="" src="Images/logo003.png" /></a>
        </div>
        </div>
    </div>
      
      <div class="overlays" style="width: 500px; margin: 0pt auto;">
      <div class="item" id="overlay01" >
        
             <img alt="" src="Images/logo001.png" />
             <p class="text">
             pinch to close pinch to close pinch to close pinch to close
             </p>
        
      </div>
             
      <div class="item" id="overlay02" >
        
             <img alt="" src="Images/logo002.png" />
             <p class="text">
             pinch to close pinch to close pinch to close pinch to close
             </p>
      
      </div>
             
      <div class="item" id="overlay03" >
             <img alt="" src="Images/logo003.png" />
             <p class="text">
             pinch to close pinch to close pinch to close pinch to close
             </p>
        
     </div>
   </div>
</body></html>

In the body structure we have a div with the class “pics” which holds the a set of images and a div with the class “overlays” which holds the overlay elements that are going to be shown when an image is clicked

Now we have to define the JavaScript code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<script type="text/javascript">
$(function() {
    $(".pics a").each(function(){
        $(this).click(function(){
            var overlay = $(this).attr('href');
            $(overlay).show();
        });
    });
//bind events to swipe and pinch in overlay
    $('.item').each(function(){
        $(this).bind('gesturestart',function(){gestureStart(event);});
        $(this).bind('gesturechange',function(){closeGesture(event);});
        $(this).bind('gestureend',function(){gestureEnd(event);});
    });
});
 function gestureStart( e ) {
    e.preventDefault();
 }
 function closeGesture( e ) {
    e.preventDefault();
    $('.item').hide();
 }
 
 function gestureEnd( e ) {
 }
</script>

The JavaScript code will:

  • Set up the click event for each image, with this code we are showing the corresponding overlay when the image is clicked:
1
2
3
4
5
6
$(".pics a").each(function(){
    $(this).click(function(){
        var overlay = $(this).attr('href');
        $(overlay).show();
    });
});
  • Set up the event listener for each overlay, when zoom event is detected in an item overlay, we capture it and we change the default behavior with our custom functions:
1
2
3
4
5
$('.item').each(function(){
    $(this).bind('gesturestart',function(){gestureStart(event);});
    $(this).bind('gesturechange',function(){closeGesture(event);});
    $(this).bind('gestureend',function(){gestureEnd(event);});
});
  • Define our custom functions that are going to be executed in each step of the zoom events:
1
2
3
4
5
6
7
8
9
10
11
function gestureStart( e ) {
    e.preventDefault();
}
 
function closeGesture( e ) {
    e.preventDefault();
    $('.item').hide();
}
function gestureEnd( e ) {
}

NOTE: that when the gesture starts we execute e.preventDefault(); this is done to prevent the default behavior of the browser (zoom in/out)

Check out the demo on your ipad/iphone here
Using this method, we can detect the zoom in/out of an element of the web page and execute a custom code.

Original post.

Making Your Content Consumable on Mobile Devices

January 3, 2012

Oshyn Social Media & Mobile BlogHow many times have you walked into a waiting room and whipped out your smartphone to kill the time until your name is called?  Or maybe you’ve gotten into a crowded elevator and your fellow travelers are all engrossed in their mobile devices – emailing, texting, checking sports scores, playing games, etc.  While smartphones have all but changed the way we kill time, these situations are just the scenarios users will find themselves in when they find your mobile content.

In January of 2011, users spent an average of 422 minutes browsing the web on their mobile devices each month1.  Most people who spend time browsing the web on a mobile device do so because they’re on a break from something else or they need something right now.  Either way, they’re likely to have a limited amount of time to find and consume your content.  Keeping that in mind, there are things you can do to ensure your content is easily consumable by mobile users.

What do you have to offer that people need “right now”?  Mobile users are more likely to have content in mind when they pick up their mobile device; they want to check their account balance, they want to purchase something, they want to research something quickly, or they want something to entertain them.  Now think about everything you have on your site – how much of it is relevant to a mobile user?  If you’ve got a lot of content that is better served on a full-size screen or can’t be viewed on a non-flash supported device, that content is probably better off being left out of the mobile site.  Content that isn’t essential should be reconsidered when filtering content to your mobile site.  Sift through your analytics to see what content is most viewed by visitors to your full site to determine what would be ideal to include in your mobile version.

Keep it simple.

Along with sorting through your content to filter what a mobile user is likely to need, try to keep the mobile content brief.  Not only are most mobile users usually in between other tasks, but they’re trying to read your content on a small screen.  If the content is long, they’re not going to stick around to scroll through long pages of text.

Surface your content.

Websites are filled with functionality that isn’t necessarily mobile friendly (think, hover states and deep layers of categorization).  People looking for content on your mobile site can’t hover and they don’t want to sort through layer upon layer of categorization to find the information they’re looking for.  Keep your categories simple and make sure that content is organized clearly to keep clicks to a minimum.

Only include essential images.

Many users will be accessing your content in places where they don’t have access to a wireless connection, which makes loading images a longer process than some users may be willing to wait through.  You don’t want to serve the same images on your mobile site as you do on your full site, they’re probably going to be far bigger than you really need on a mobile device.  However, if images are critical to the user experience, include them, but make sure they’re optimized for mobile devices and they’re used only where necessary.

Finally, have a way out.

There will always be someone out there who wants access to your full site, even if they know that they’re going to have to pinch, zoom, scroll, etc.  Chances are, these users know what they want to gain from your site and, if they can’t get to it on their mobile device, they may leave and go somewhere else to find the content they’re looking for.

REFERENCES

https://www.wirelessintelligence.com/analysis/2011/03/smartphone-users-spending-more-face-time-on-apps-than-voice-calls-or-web-browsing/

Original post can be found here.

CSS3 Best Practices

December 29, 2011

Always keep in mind that CSS3 is by no means supported by all browsers, every browser has different support for each CSS3 element and we need to be very careful when using these new features.

Introduction

The first thing you need to do when using a CSS3 property is do a little research. You will always find some resource online that tells you what you need to know about browser support for any CSS3 property, so be sure to Google it FIRST.

Secondly, do not get carried away with all the great stuff that you can achieve using CSS3. We are not trying to replace great designs and coding. We want to try to achieve the perfect cross-browser markup and styling.

Right now I like to think of CSS3 as “enhancements” for your website’s look. The most important thing on your site is functionality and accessibility. Never sacrifice any of these two for a “nicer” look. You need to keep in mind that everything should be fully functional if you remove all your CSS3 and your site should still look good.

Fallback

One of the most important best practices when using CSS3 is to provide fallback options every time it’s possible. You will find the ability to do this when using backgrounds or fonts, among other things.

Animations

I do not recommend using animations quite yet, but if you totally want to use them, make sure that your element will look just fine without the animation, because it is very likely that it won’t work.

Code Maintenance

We need to think on the future when coding CSS3. To achieve a certain cross-browser property you will need to add some vendor-specific code. I recommend adding comments or creating a file where you keep track of these lines of code. This way you will be able to remove them when supported on all browsers. I don’t know when, but I really hope a standard gets out for all browsers soon.

Fonts

@Font-face property is one of the more supported elements and you can achieve a cross-browser behavior, this is why a lot of programmers are using @font-face. We need to be sure that the declared fonts are totally legible on our websites. And one more thing, always declare a fallback font that approximately matches your font in size. Click here to read my post about how to make your @font-face compatible across multiple browsers (link to post).

Conclusion

CSS3 is not quite complete yet, so be very careful when using CSS3 properties. And most important of all, test your styles on all major browsers (Internet Explorer, Safari, Chrome, Firefox, Opera) before permanently applying them to your website. A good rule of thumb is: the more versions, the better.

Original post can be found here.

Follow

Get every new post delivered to your Inbox.