Tuesday, March 29, 2011

Refresh Javascript

Here are my favourite links to refresh or introduce Javascript to new comers.

http://www.jslint.com/lint.html

http://vimeo.com/18511621

http://developer.yahoo.com/yui/theater/ by Douglas Crockford

 

Javascript is a wild language which need to be tamed.
Here are some js pattern which I think best pattern to tackle down the scoping and closure issue which called (Module Pattern)

http://stackoverflow.com/questions/3722820/examples-of-practical-javascript-object-oriented-design-patterns

Recently I build a framework called SiteUI which follows this pattern and give you protection and flexibility.

SiteUI.BaseUI = function (options)
{
//default options
var defaultsOpt = {
//set it to false otherwise, when you disable from the ajax, it when postback it will enable all your control
//but if you have other reason you can do so.
disableAllButtonOnSubmit: false,
ajaxProcessMessage: "Processing..."
};

var options = $.extend(defaultsOpt, options);


//optional value - to disable all button on submit or not
var disableAllButtonOnSubmit = options.disableAllButtonOnSubmit;
var setDisableAllButtonOnSubmit = function (value)
{
disableAllButtonOnSubmit = value;
}

var ajaxProcessMessage = options.ajaxProcessMessage;
var setAjaxProcessMessage = function (value)
{
ajaxProcessMessage = value;
}

//Shopping Cart Control
var shoppingCartControl = null;

var ddlPostbackSelector = '.ddlPostback';
var txtPostbackSelector = '.txtPostback';
var btnPostbackSelector = '.btnPostback';
var chkPostbackSelector = '.chkPostback';

var loadingSelector = '.postbackLoading';
var loaderHtml = "<img src='/images/ajax-loader.gif' class='postbackLoading' style='width:15px;height:15px;position:absolute; z-index:9999;' />";



var isHideLoadingBeforePostbackTemporary = false;
//this to indication a form validator after call back - (CommonFormValidator.js option)
var formValidator_CallBack = function (source, isValid)
{
//alert('formValidator_CallBack : ' + isValid);
if (!isValid)
{
//not valid - hide loading temporary - because this will be called first before click happen
isHideLoadingBeforePostbackTemporary = true;
}
};

//Show loading
var showLoadingBeforePostback = function (source, addLoadingToParent)
{
//alert('showLoadingBeforePostback isHideLoadingBeforePostbackTemporary: ' + isHideLoadingBeforePostbackTemporary);
if (!isHideLoadingBeforePostbackTemporary)
{
//add loader html after
if (addLoadingToParent)
{
$(source).parent().after(loaderHtml);
showPageLoader(); //show page loader
}
else
{
$(source).after(loaderHtml);
showPageLoader(); //show page loader
}
}
else
{
//set it to false again
isHideLoadingBeforePostbackTemporary = false;
}
};

//Clear Loading Function
var clearLoading = function ()
{
$(loadingSelector).remove();
hidePageLoader();
};

//show Page loader - this using jquery.loader this will disable all the button automatically
var showPageLoader = function ()
{
$.loader({
className: "loaderBox",
content: ''
});

};

//hide page loader
var hidePageLoader = function ()
{
$.loader('close');
};

//Show Hide Loading On Content
var showHideLoadingOnContent = function (contentSelector, isShow)
{
if (isShow)
{
$(contentSelector).before(loaderHtml); //add loader after it fades

//show loading
showPageLoader(); //show page loader to hide
$(contentSelector).animate({ opacity: 0 }, 150);
//$(contentSelector).effect('fade',null,500);

}
else
{
//hide loading - remove all loading selector
$(loadingSelector).remove(); //remove loader first
hidePageLoader(); // hide page loader
//just in case if in the beginning it hides this
$(contentSelector).animate({ opacity: 1 }, 150);
//$(contentSelector).effect('fade',null,500);
}
};

//redirect content
var redirectContent = function (url, contentSelector)
{
if (contentSelector)
{ //only if they pass content selector we can redirect with show and hide loading
showHideLoadingOnContent(contentSelector, true);
}

window.location = url;
};

//update content
var updateContent = function (responseText, contentSelector, updateContent_Callback)
{

$(contentSelector).html(responseText);


if (updateContent_Callback)
updateContent_Callback(responseText);

//hide loading on content
showHideLoadingOnContent(contentSelector, false);
};

//Used to track last Request Ajax URL
var lastRequestAjaxUrl = "";
//load content
var loadContent = function (ajaxUrl, relatedData, contentSelector, updateContent_Callback)
{
showHideLoadingOnContent(contentSelector, true);

lastRequestAjaxUrl = ajaxUrl;
$.ajax({
type: "POST",
url: ajaxUrl,
data: relatedData,
complete: function (data, stat)
{
//only process last request ajax url
if (this.url == lastRequestAjaxUrl)
{
//$.log("Update this " + this.url + " VS " + lastRequestAjaxUrl);
if (stat == "success")
{
var isError = true;
updateContent(data.responseText, contentSelector, updateContent_Callback);
}
else
{
updateContent("<p>Error Loading Data : " + stat + ' : ' + data.responseText + "</p>", contentSelector, updateContent_Callback);
}
}
else
{
//$.log("Dispose this ajax " + this.url + " VS " + lastRequestAjaxUrl);
}
}
});
};


// *************** MICROSOFT AJAX INIT - HANDLE ALL AJAX CALL*******************
var initMicrosoftAjaxRequest = function ()
{
/// <reference name="MicrosoftAjax.js"/>
if (typeof (Sys) != 'undefined' && typeof (Sys.WebForms) != 'undefined')
{
Sys.Application.add_init(microsoftAJAX_App_Init);
}
};

var microsoftAJAX_App_Init = function ()
{
Sys.WebForms.PageRequestManager.getInstance().add_beginRequest(microsoftAJAX_BeginRequest);
Sys.WebForms.PageRequestManager.getInstance().add_endRequest(microsoftAJAX_EndRequest);
Sys.WebForms.PageRequestManager.getInstance().add_pageLoaded(microsoftAJAX_pageLoaded);
};

var postbackTriggerElement;
var microsoftAJAX_BeginRequest = function (sender, args)
{
if (disableAllButtonOnSubmit)
{
//disable all button
disableAllSubmitButtons(true);
}
postbackTriggerElement = args.get_postBackElement()
var ctrl = $(postbackTriggerElement);

//remove all validation error
removeAllValidationError();

if (ctrl.is("input:checkbox") || ctrl.is("input:radio"))
{
ctrl.hide(); //hide this check box
if (!ctrl.hasClass("noLoaderOnPostback"))
{
showLoadingBeforePostback(ctrl, true); //show loading on this control
}
}
else if (ctrl.is("input:submit"))
{ //you can change it to other
if (ctrl.hasClass("waitLabelOnPostback"))
{
if (ajaxProcessMessage != "")
{
ctrl.val(ajaxProcessMessage);
}
}

if (ctrl.hasClass("disableOnPostback"))
{
ctrl.attr('disabled', true); //disable but later on postback it will enable or disable based on the behaviour
}


if (!ctrl.hasClass("noLoaderOnPostback"))
{
showLoadingBeforePostback(ctrl);
}
}
else
{ //other items show loading
//show loading on this control
if (!ctrl.hasClass("noLoaderOnPostback"))
{
showLoadingBeforePostback(ctrl);
}
}
};

var microsoftAJAX_pageLoaded = function (sender, args)
{
if (!postbackTriggerElement)
{
return;
}

//get updated panels and referesh
var updatedPanels = args.get_panelsUpdated();
for (i = 0; i < updatedPanels.length; i++)
{
//apply filter textbox
setFilterTextbox(updatedPanels[i].id);
//set rating
setRating(updatedPanels[i].id);
//set date picker
setDatePicker(updatedPanels[i].id);
//set text area counter
setTextAreaCounter(updatedPanels[i].id);

//Call Microsoft AJAX_EndRequest_AdditionalFunciton() if available
if (typeof (microsoftAJAX_PageLoaded_AdditionalFunction) == 'function')
{
microsoftAJAX_PageLoaded_AdditionalFunction(updatedPanels[i].id);
}
}
};


//end request
var microsoftAJAX_EndRequest = function (sender, args)
{
if (args.get_error() != undefined)
{
var errorMessage = args.get_error().message;
args.set_errorHandled(true);
alert("Error in ajax call : " + errorMessage);
clearLoading(); //clear loading
return;
}


if (disableAllButtonOnSubmit)
{
//enable all button
disableAllSubmitButtons(false);
}




//Call Microsoft AJAX_EndRequest_AdditionalFunciton() if available
if (typeof (microsoftAJAX_EndRequest_AdditionalFunction) == 'function')
{
microsoftAJAX_EndRequest_AdditionalFunction();
}


//empty the postback element
postbackTriggerElement = null;

hidePageLoader(); //hide page loader
};


//this can be override by calling set ajax page loaded
var microsoftAJAX_PageLoaded_AdditionalFunction = function (updatedPanelId)
{
//baseUI. microsoft AJAX Page Loaded Additional funxtion
//you can write default function here
};

//this can be override by calling set ajax end request
var microsoftAJAX_EndRequest_AdditionalFunction = function ()
{
//baseUI. microsoft AJAX End Request Additional funxtion
//you can write default function here
};

//override ajax end request
var overrideMicrosoftAJAX_EndRequest_AdditionalFunction = function (func)
{
microsoftAJAX_EndRequest_AdditionalFunction = func;
};

//override ajax page loaded
var overrideMicrosoftAJAX_PageLoaded_AdditionalFunction = function (func)
{
microsoftAJAX_PageLoaded_AdditionalFunction = func;
};
//-----------------------------------------------



//use for postback
var setupPostbackControlEvents = function ()
{
//show loading before postback
$(ddlPostbackSelector).live("change", function (e)
{
//e.preventDefault();//cannot prevent default, it won't go to onclick
showLoadingBeforePostback(this);
});

//filter search keyword
$(txtPostbackSelector).live("change", function (e)
{
//e.preventDefault();//cannot prevent default, it won't go to onclick
showLoadingBeforePostback(this);
});

$(btnPostbackSelector).live("click", function (e)
{
//e.preventDefault(); //cannot prevent default, it won't go to onclick
showLoadingBeforePostback(this);

if ($(this).hasClass("clearValidation"))
{
//check if it has class clear validation
removeAllValidationError();
}

});


$(chkPostbackSelector + " input").live("change", function (e)
{
//e.preventDefault(); //cannot prevent default, it won't go to onclick
showLoadingBeforePostback(this, true);
removeAllValidationError(); //it will clear validation - usually this will change the view
});
};


//setup Dropdown Item
var setupDropdownItem = function ()
{

//find drop down item trigger
$('.dropdownItemTrigger').each(function ()
{
// Make the drop down cart appear/disappear.
var boxSelector = $(this).attr('rel');

// Set natural height and other properties of hidable object.
$(boxSelector).data('naturalHeight', $(boxSelector).height());
$(boxSelector).addClass('hidden');
$(boxSelector).css(
{
display: 'none',
bottom: 'auto'
});

// Create back reference.
$(boxSelector).data('trigger', $(this));
});

$('.dropdownItemTrigger').live("click", function (e)
{
e.preventDefault();

var $trigger = $(this);

// Don't do anything if the drop down item is already animating.
if ($trigger.hasClass('disabled')) return false;

// Find direction.
var direction = $trigger.hasClass('maximise') ? 1 : -1;

// Find effect type.
var classes = $trigger.attr('class'); //.match(/effect-/);
var effect = $trigger.attr('class').match(/\beffect-[a-z]{1,}/);
//alert(effect);

// Make the drop down cart appear/disappear.
var boxSelector = $trigger.attr('rel');



if (boxSelector == shoppingCartControl.shoppingCartControlSelector)
{
shoppingCartControl.checkToReload();
}

$(boxSelector).trigger('dropdowntoggle', [direction, effect]);
});

$('.dropdownItem').live('dropdowntoggle', function (e, direction, fxtype)
{
var $dropdownItem = $(this);
var $trigger = $dropdownItem.data('trigger');


// Ensure direction.
if (isNaN(direction)) direction = $dropdownItem.hasClass('shown') ? -1 : 1;

// Disable trigger.
$trigger.addClass('disabled');

// Set states. / cursor
$trigger.toggleClass('maximise', direction == -1);
$trigger.toggleClass('minimise', direction == 1);

$dropdownItem.toggleClass('hidden', direction == -1);
$dropdownItem.toggleClass('shown', direction == 1);

// Effect
//var currPosition = $dropdownItem.css('top').replace(/([a-z]+)/, '') * 1
var currPosition; // = direction == 1 ? $dropdownItem.height() * -1 : 0;
var displacement; // = $dropdownItem.height(); //$dropdownItem.data('naturalHeight');
var newPosition; // = currPosition + displacement * direction;

//alert(fxtype);
// Toggle drop down item + Effect
if (fxtype == 'effect-slide')
{
// Calculate new values.
currPosition = direction == 1 ? ($dropdownItem.height() * -1) : 0 - 50;
displacement = $dropdownItem.height(); //$dropdownItem.data('naturalHeight');
newPosition = currPosition + displacement * direction;

// Execute
$dropdownItem.css(
{
display: 'block',
top: currPosition
}).animate(
{
top: newPosition
}, 400, function ()
{
$trigger.toggleClass('active', direction == 1);

$trigger.removeClass('disabled');
});
}
else
{
// if (fxtype=='effect-fade') {
// Calculate new values.
var currOpacity = direction == 1 ? 0 : 1;
var newOpacity = direction == 1 ? 1 : 0;


//Execute
$dropdownItem.css(
{
display: 'block',
top: 0,
opacity: currOpacity
}).animate(
{
opacity: newOpacity
}, 400, function ()
{
$trigger.toggleClass('active', direction == 1);
if (direction == -1)
{
//hide the display
$dropdownItem.css({display: 'none'});
}
$trigger.removeClass('disabled');
});
}

});
};

/*this doesn't work for shopping cart in IE 6*/
var setupDropdownItemUsingJqueryUI = function ()
{
//Bind Drop down item - just require this button
$('.dropdownItemTriggerBlind').live("click", function (e)
{
e.preventDefault();

var $trigger = $(this);
// Make the drop down cart appear/disappear.
var boxSelector = $trigger.attr('rel');

//check if it is shopping cart control
if (boxSelector == shoppingCartControl.shoppingCartControlSelector)
{
shoppingCartControl.checkToReload();
}


//using jquery.ui toggle( effect, [options], [speed], [callback] )
$(boxSelector).toggle('blind', null, 300);

});

}


/*Setup Events - This will be call unsynchronously*/
var setupEvents = function ()
{ //setup postback control events
setupPostbackControlEvents();
//setup drop down
setupDropdownItem();



//for login trigger
$(".txtTriggerLogin").live("change", function (e)
{
$(".btnLogin").click();
});

$(".txtTriggerLogin").live("keypress", function (e)
{
//$.log('run from DISABLE DEFAULT BUtton');
return runFunctionOnEnter(e,
function ()
{
$(".btnLogin").click();
}
);
});


// Removes SEO text in navItems
$(".clearText").html("");
$(".clearText").removeClass("clearText");

// Clears input fields on Focus
// Title must match the value to clear.
$(".clearField").live("blur", function ()
{
if ($(this).val() == "")
{
$(this).val($(this).attr("title"));
};
});

$(".clearField").live("focus", function ()
{

if ($(this).val() == $(this).attr("title"))
{
$(this).val("");
};
});


//bind shopping cart
//cart = new SiteUI.ShoppingCart();
//cart.updateCartHover();
};

/*Constructor*/
var bindDefaultBehaviour = function ()
{
//init ajax request behaviour
initMicrosoftAjaxRequest();
//disable default button
disableDefaultButton();

//set droppy menu
$('#dropdownNavigation').droppy();

shoppingCartControl = new SiteUI.ShoppingCartControl();

//run this async
setTimeout(function ()
{
setupEvents();


//apply filter textbox
setFilterTextbox();

setRating();
setDatePicker();
setTextAreaCounter();

}, 0);
};

var init = function ()
{

}

//return public method
return {
bindDefaultBehaviour: bindDefaultBehaviour,
formValidator_CallBack: formValidator_CallBack,
redirectContent: redirectContent,
setupPostbackControlEvents: setupPostbackControlEvents,

loadContent: loadContent,
updateContent: updateContent,

clearLoading: clearLoading,
showLoadingBeforePostback: showLoadingBeforePostback,
showHideLoadingOnContent: showHideLoadingOnContent,

showPageLoader: showPageLoader,
hidePageLoader: hidePageLoader,

setAjaxProcessMessage: setAjaxProcessMessage,
setDisableAllButtonOnSubmit: setDisableAllButtonOnSubmit,
overrideMicrosoftAJAX_EndRequest_AdditionalFunction: overrideMicrosoftAJAX_EndRequest_AdditionalFunction,
overrideMicrosoftAJAX_PageLoaded_AdditionalFunction: overrideMicrosoftAJAX_PageLoaded_AdditionalFunction
};
}



Monday, March 28, 2011

Page Load VS Init

 

Most web developer already inherits the wrong culture of population of data.
After they graduate from college, without knowing and really understand of page life cycle and state management, They are prune to inherits the bad habits of the culture.

Here are some of the culture which you may still see it in your current project to populate an item.


<asp:DropdownList id="lstStates" runat="server" DataTextField="StateName" DataValueField="StateCode" />

protected override void OnLoad(EventArgs args)
{
if(!this.IsPostback)
{
this.lstStates.DataSource = QueryDatabase();
this.lstStates.DataBind();
}
base.OnLoad(e);
}





As is the nature of databound controls in ASP.NET, the state dropdown will be using ViewState to remember it's databound list of list items.



At the time of this ranting, there are a total of States or countries. Not only does the dropdown list contain a ListItem for each and every state, but each and every one of those states and their state codes are being serialized into the encoded ViewState.



That's a lot of data to be stuffing down the pipe every time the page loads, especially over a dialup connection. I often wonder what it would be like if I explained to my grandmother the reason why her internet is so slow is because her computer is telling the server what all the States / Countries are. Too bad... those extra states and countries dropdown list are really wearing down your bandwidth.



Like the problem with static data, the general solution to this problem is to just disable ViewState on the control. Unfortunately, that is not always going to work. Whether it does depends on the nature of the control you are binding, and what features of it you are dependant on.



In this example, if we simply added EnableViewState="false" to the dropdown, and removed the if(!this.IsPostback) condition, we would successfully remove the state data from ViewState, but we would immediately run into a troubling problem.



The dropdown will no longer restore it's selected item on postbacks.



WAIT!!! This is another source of confusion with ViewState.


The reason the dropdown fails to remember it's selected item on postbacks is NOT because you have disabled ViewState on it.





Postback controls such as dropdownlist and textbox restore their posted state (the selected item of a dropdown ist 'posted') even when ViewState is disabled, because even with ViewState disabled the control is still able to post its value.





It forgets it's selected value because you are rebinding it in OnLoad, which is after the dropdown has already loaded it's posted value. When you databind it again, the first thing it does is throw that into the bit bucket (you know, digital trash). That means if a user selects California from the list, then click on a submit button, the dropdown will stubbornly return the default item (the first item if you don't specify it otherwise).





Thankfully, there is an easy solution: Move the DataBind into OnInit:




<asp:DropdownList id="lstStates" runat="server"   DataTextField="StateName" DataValueField="StateCode" EnableViewState="false" />

protected override void OnInit(EventArgs args)
{
this.lstStates.DataSource = QueryDatabase();
this.lstStates.DataBind();
base.OnInit(e);
}





Yes this will call QueryDatabase everytime you postback but you still can be solved it using simple caching instead of sacrificed the traffic performance in View state.

MVP or MVC

 

Recently, I am trying a very cool new architecture called WebForm MVP which comes as a mediator between Webform and MVC.

This framework is very brilliant and explain the best practice for using Webform.

For example the reason of using Page Init instead of Page load in regards of the viewstate.
This will enforce the best practice for the web developer to build there web application which now fully support for unit testing.

You can check this at http://webformsmvp.com/
and here is the contrib project which support common use presenter and Ioc Container such as Ninject and structure map.
http://webformsmvpcontrib.codeplex.com/

I believe this project will soon be adopted by VS next release.

Here is some brilliant article supported for this.
Viewstate - http://weblogs.asp.net/infinitiesloop/archive/2006/08/03/Truly-Understanding-Viewstate.aspx

Friday, February 11, 2011

Ghost node solve the crawling problem

 

Finally After solve the ghost node, My crawling search start working again.

This query safe my time.


SELECT * FROM umbracoNode, 
WHERE
nodeObjectType = 'C66BA18E-EAF3-4CFF-8A22-41B16D66A972' -- that are of type 'Content'
AND
umbracoNode.id NOT IN (SELECT nodeId FROM cmsContent) -- but are not in the 'Content' table

Fix for No Document exists with Version '00000000-0000-0000-0000-000000000000' when sorting

 

 

Finally I found what may cause the problem.

It’s because I installed runway sitemap and in some reason, it create a ghost node

http://our.umbraco.org/forum/getting-started/questions-about-runway-and-modules/10685-No-Document-exists-with-Version-%2700000000-0000-0000-0000-000000000000%27?p=0#comment40259

Do a select first to check


select * FROM umbracoNode
WHERE
umbracoNode.nodeObjectType = 'C66BA18E-EAF3-4CFF-8A22-41B16D66A972' -- that are of type 'Content'
AND
umbracoNode.id NOT IN (SELECT nodeId FROM cmsContent) -- but are not in the 'Content' table

===

and delete that node.


Delete FROM umbracoNode
WHERE
umbracoNode.nodeObjectType = 'C66BA18E-EAF3-4CFF-8A22-41B16D66A972' -- that are of type 'Content'
AND
umbracoNode.id NOT IN (SELECT nodeId FROM cmsContent) -- but are not in the 'Content' table

Friday, January 28, 2011

Umbraco Debug Script


Add this to your bookmark can be easier to debug Umbraco Macro error

javascript:window.location=window.location+'?umbDebugShowTrace=true';

Wednesday, January 12, 2011

Session in Umbraco

After looking around to fix things, finally I reliase that my session is always null in Umbraco.

Which is unexpected accident for umbraco, and after couple hours, luckily I found the reason and how to solve it.

Here is the reference.

http://our.umbraco.org/forum/developers/extending-umbraco/4433-Accessing-Session-State-in-User-Control

you just need to add in web.config if you use IIS7

<modules runAllManagedModulesForAllRequests="True">

Tuesday, January 11, 2011

Alternative not to use Captcha

 

· Reason why not to use captcha

  • Most people hate filling out Captchas.
  • It doesn't stop human spammers (they fill out your form manually).
  • Spam software these days can break through simple Captchas.
  • If you make your Captcha harder, you may block legit users.
  • Blind people can't get past Captchas.

 

· Simple solution just add 1 more input field hidden by CSS – this need to be check if it is filled means it’s filled by bot.

http://damienkatz.net/2007/01/negative_captch.html

· More explanation : http://nedbatchelder.com/text/stopbots.html

Wednesday, November 17, 2010

uComponent State Fix Bugs


Recently I have to fix this bug to enable the view state after postback.

 

Change the text to use Literal intead of LiteralControl.
Literal will store the view state.

In SelectedItemsTemplate.cs

Change selectedNodeText = new Literal()

var selectedNodeText = new Literal();
selectedNodeText.ID = "SelectedNodeText";
innerDiv.Controls.Add(selectedNodeText);





and when binding change it to grab Literal instead of LiteralControl




void SelectedValues_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
HtmlGenericControl liSelectNode = (HtmlGenericControl)e.Item.FindControl("SelectedNodeListItem");
HtmlAnchor lnkSelectNode = (HtmlAnchor)e.Item.FindControl("SelectedNodeLink");
Literal litSelectNodeName = (Literal)e.Item.FindControl("SelectedNodeText");
HtmlAnchor infoButton = (HtmlAnchor)e.Item.FindControl("InfoButton");





 



OnLoad of MNTP_DataEditor, add this before Binding Selected values




protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);

//add the js/css required
this.AddAllMNTPClientDependencies();

//KK.Fix for postback binding data
if (this.Page.IsPostBack)
{
//if it is postback - we need to bind it from selectedIds
//setting XML Value will give the data source and will trigger proper binding
XmlValue = ConvertToXDocument(SelectedIds);

//When first load this is run on populate form, so it has set the selectedIds.
//but on postback / this value is taken from the client and it must be setup whenever it loads it.
//So I decide to put it here.. when it is postback, we will grab the data from front end and sycn it to the control
//if it is not postback, we setup nothing except before this run, someone set it on populate the form.
}

//bind the repeater if theres a data source
if (SelectedValues.DataSource != null)
{
SelectedValues.DataBind();
}





 



Done…

Tuesday, November 16, 2010

Umbraco Consistency– (Jtree)

 

Today, Finally what happen on my Custom Tree State.
In some reason when in my custom tree, It forget about their state.

And After couple hours, I notice that it’s because I use Pascal Case on my application name. 

In some Umbraco.Tree it store the the state using lowercase whatever appname in database but when i loaded it used whatever the name in database.

Here is the screenshot

shiftApp: function (whichApp, appName) {
89 /// <summary>Changes the application</summary>
90
91 this._debug("shiftApp: " + whichApp + ", " + appName);
92
93 UmbClientMgr.mainTree().saveTreeState(this._currApp == "" ? "content" : this._currApp);
94
95 this._currApp = whichApp.toLowerCase();
96
97 if (this._currApp != 'media' && this._currApp != 'content' && this._currApp != 'member') {
98 jQuery("#buttonCreate").attr("disabled", "true");
99





The reason why I use Pascal Case instead of Camel Case because in .NET  I Store the appAlias and TreeAlias in Enum, So It seems good if I use Pascal Case for consistency.



I guess I need to put it back to Camel Case, otherwise I need to change UmbracoApplicationActions.js for this to work

Wednesday, November 10, 2010

Jgrid with REST Json Webservice

 

Here is the example of integrate your Jgrid with REST webservice.

 

Firstly in your HTML Page. you just need this.

<asp:Content ID="Header" ContentPlaceHolderID="head" runat="server">
<link href="/scripts/js/jQuery/jqGrid/themes/ui.jqgrid.css" rel="stylesheet" type="text/css" />
<link href="/scripts/js/jQuery/jqGrid/themes/redmond/jquery-ui-1.8.2.custom.css"
rel="stylesheet" type="text/css" />
<!-- must have locale before jqGrid-->
<script src="/scripts/js/jQuery/jqGrid/js/i18n/grid.locale-en.js" type="text/javascript"></script>
<script src="/scripts/js/jQuery/jqGrid/js/jquery.jqGrid.min.js" type="text/javascript"></script>

<script type="text/javascript" language="javascript">

//Get Data For JQGrid because we use data Service
function getData(postData) {

//add more parameter
postData.pCatId = '<%=RequestProductCategoryID %>';

//show loading button
$("#load_jqGridTable").show();

//alert(JSON.stringify(postData));

jQuery.ajax({
type: "POST",
url: "/usercontrols/CMS/Webservices/DataService.asmx/LoadJqGridDataProductList" ,

data: JSON.stringify(postData), //we can inject whatever data here
contentType: "application/json; charset=utf-8",
dataType: "json",
//success: function (msg) { alert('success' + msg); },
//use this complete.
complete: function (data, stat) {

//hide
$("#load_jqGridTable").hide();

if (stat == "success") {
var jg = $("#jqGridTable")[0];
//alert('jg : ' + jg);
//note. this example is from a NON-ASP.Net web service.
//there your data is wrapped into dictionary with only one key - '

var t = JSON.parse(data.responseText)['d'];
//alert('t : ' + t);
//alert('t.Name :' + t[0].Name);

//var str = {"page":"1","total":"1","records":"1","rows":[{"id":"1","cell":["True","Product1","1","random"]}]};
//jg.addJSONData(str);

jg.addJSONData(t);
}
else
{
alert('loading data: ' + stat + ' : ' + data.responseText);
}

}
/* this will be handle inside complete
error: function (context) {
alert('error loading data: ' + context.responseText);
}
*/
});
}


jQuery(document).ready(function () {
//jqGrid Defination
jQuery("#jqGridTable").jqGrid({
// the url parameter tells from where to get the data from server
// adding ?nd='+new Date().getTime() prevent IE caching
//url: requestURL,
// datatype parameter defines the format of data returned from the server
// in this case we use a JSON data
//datatype: "json",

//We use JSON Web Service so need to use this
datatype: getData,
// colNames parameter is a array in which we describe the names
// in the columns. This is the text that apper in the head of the grid.
//colNames: ['Publish', 'Name', 'ID', 'Last Modified','ActionLinks'],
colNames: ['Active', 'Name', 'ID', 'Last Modified'],

// colModel array describes the model of the column.
// name is the name of the column,
// index is the name passed to the server to sort data
// note that we can pass here nubers too.
// width is the width of the column
// align is the align of the column (default is left)
// sortable defines if this column can be sorted (default true)
colModel: [
{ name: 'Active', index: 'Active', width: 60, align: "center" },
{ name: 'Name', index: 'Name', width: 470 },
{ name: 'ProductID', index: 'ProductID', width: 95 },
{ name: 'LastModifiedDate', index: 'LastModifiedDate', width: 95 }
//{ name: 'ActionLinks', index: 'ActionLinks', width: 195, sortable: false }
],

// pager parameter define that we want to use a pager bar
// in this case this must be a valid html element.
// note that the pager can have a position where you want
pager: '#jqGridPager',
// rowNum parameter describes how many records we want to
// view in the grid. We use this in example.php to return
// the needed data.
rowNum: 30,
// rowList parameter construct a select box element in the pager
// in wich we can change the number of the visible rows
rowList: [10, 20, 30],
// path to mage location needed for the grid
//imgpath: rootFolder + '/js/jqGrid/themes/bCommerce/images',
//viewrecords defines the view the total records from the query in the pager
//bar. The related tag is: records in xml or json definitions.
viewrecords: true,
caption: "Products",
//height: "100%",
hidegrid: true,
// Set initial grid view.
page: 1,
// sortname sets the initial sorting column. Can be a name or number.
// this parameter is added to the url
sortname: 'Name',
//sets the sorting order. Default is asc. This parameter is added to the url
sortorder: 'asc',
// Events.
gridComplete: function () {
//alert('grid complete');

// Show items not found message.
if (jQuery("#jqGridTable").getGridParam("records") == "0")
jQuery("#jqGridEmptyRecordSet").css('display', 'block');
else
jQuery("#jqGridEmptyRecordSet").css('display', 'none');

// Set width of empty record set to grid width.
jQuery("#jqGridEmptyRecordSet").css('width', jQuery("#jqGridTable").css('width'));
},

});


jQuery("#jqGridTable").jqGrid('navGrid', '#jqGridPager', { edit: false, add: false, del: false });

});
/*
jQuery("#list").jqGrid(
{
url: 'server.php?q=2',
datatype: "json",
colNames: ['Active', 'ProductID', 'Product Name', 'Last Modified', 'Edit ', 'View'],
colModel: [
{ name: 'id', index: 'id', width: 55 },
{ name: 'invdate', index: 'invdate', width: 90 },
{ name: 'name', index: 'name asc, invdate', width: 100 }, { name: 'amount', index: 'amount', width: 80, align: "right" },
{ name: 'tax', index: 'tax', width: 80, align: "right" }, { name: 'total', index: 'total', width: 80, align: "right" }, { name: 'note', index: 'note', width: 150, sortable: false}],

rowNum: 10, rowList: [10, 20, 30],
pager: '#pager',
sortname: 'id', viewrecords: true, sortorder: "desc", caption: "JSON Example" });

jQuery("#list2").jqGrid('navGrid', '#pager2', { edit: false, add: false, del: false });

*/

</script>
</asp:Content>
<asp:Content ID="Content" ContentPlaceHolderID="body" runat="server">
<umb:UmbracoPanel ID="Panel1" runat="server" hasMenu="false" Text="">

<table id="jqGridTable"></table>
<div id="jqGridEmptyRecordSet">No items found.</div>
<div id="jqGridPager"></div>

</umb:UmbracoPanel>


</asp:Content>





 



Finally Create your REST JSON Web Service





/// <summary>
/// Don't change this name attribute, coz this naming is related to jqGrid Spec
/// </summary>
public class JqGridRow
{
public string id { get; set; }
public List<object> cell { get; set; }

public JqGridRow(string id, List<object> cells)
{
this.id = id;
this.cell = cells;
}
}

/// <summary>
/// Don't change this name attribute, coz this naming is related to jqGrid Spec
/// </summary>
public class JqGridData
{
public int page { get; set; }
public int total { get; set; }
public int records { get; set; }
public List<JqGridRow> rows { get; set; }

public JqGridData(int pageNum, int totalPage, int totalRecords, List<JqGridRow> rows)
{

this.page = pageNum;
this.total = totalPage;
this.records = totalRecords;
this.rows = rows;

}
}


/// <summary>
/// Summary description for DataService
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ToolboxItem(false)]
// To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.
[ScriptService]
public class DataService : System.Web.Services.WebService
{
/// <summary>
/// This method for mapping the parameter from json, You can't change this because it will make an error in loading the data
/// Optional Parameter does not work as well. I Wish the optional parameter will work for this for the next version
///
/// Remember this is also Case sensitive
/// "_search":false,"nd":1289313832212,"rows":30,"page":1,"sidx":"Name","sord":"asc"
/// </summary>
/// <param name="page"></param>
/// <returns></returns>
[WebMethod]
[ScriptMethod]
public JqGridData LoadJqGridDataProductList(bool _search, int page, int rows, string sidx, string sord, int pCatId)
{
return LoadJqGridDataProductListProcess(_search, page, rows, sidx, sord, pCatId);
}

/// <summary>
/// This is the actual parameter
/// </summary>
/// <param name="isSearch"></param>
/// <param name="page"></param>
/// <param name="rows"></param>
/// <param name="sidx"></param>
/// <param name="sord"></param>
/// <param name="pCatId"></param>
/// <returns></returns>
private JqGridData LoadJqGridDataProductListProcess(bool isSearch = false,int page = 1,int rows = 30,string sidx = "", string sord = "", int pCatId = -1)
{
// int page = Convert.ToInt32(HttpContext.Current.Request["page"]);

try
{
//optimize it because using specific field
var lst = ProductRepository.FindAll();


//Generate jgGrid Rows
List<JqGridRow> gRows = new List<JqGridRow>();
List<object> cells = new List<object>();
foreach (var d in lst)
{
cells = new List<object>();
cells.Add(d.Active);
cells.Add(d.Name);
cells.Add(d.ProductID);
cells.Add(d.LastModifiedDate.ToString(CommonHelper.ShortDateTimeFormat));
gRows.Add(new JqGridRow(d.ProductID.ToString(), cells));
}

//Generate
return new JqGridData(1, 1, 1, gRows);

}
catch (Exception ex)
{
//just throw it at this time
throw ex;
}
return null;
}


}





 



Note: I wish this optional parameter will work for binding JSON object directly.




private JqGridData LoadJqGridDataProductListProcess(bool isSearch = false,int page = 1,int rows = 30,string sidx = "", string sord = "", int pCatId = -1)



Monday, November 01, 2010

Umbraco New Section Part 1

 

Here is the list of note for creating a custom section in Umbraco

Update Client Name

Change Umbraco/umbraco.aspx title

clip_image002

Change umbraco_client/application/umbracoApplicationAction.js

clip_image004

CUSTOM SECTION in UMBRACO

Creating Custom Section

(Icon)

- Update tray sprite icon image /umbraco/images/tray/traySprites.png
To Add a new icon, just insert 72 pixel of an icon

- Update CSS /umbraco/css/umbracoGui.css

/* CUSTOM TRAY */

.trayinventory{background-position: -18px -594px;}

/* END CUSTOM TRAY*/

Add Images for tree item

umbraco\images\umbraco\custom

(Database)

Add row in UmbracoApp

- Inventory

clip_image006

Add row in umbracoAppTree (to set up the tree)

clip_image008

Add User App Permission

clip_image010

need to setup the language so it will say Inventory instead of [Inventory]

Change umbraco/config/lang/en.xml
<area alias="sections">

<!-- Custom section -->

<key alias="inventory">Inventory</key>

<!-- End Custom Section-->

</area>

Fix undefined in the title ? (need to refresh javascript)

Add a task
Edit umbraco/config/create/ui.xml

<!-- CUSTOM SECTION -->

<nodeType alias="inventoryProduct">

<header>Product</header>

<usercontrol>/create/simple.ascx</usercontrol>

<tasks>

<create assembly="umbraco" type="userTasks" />

<delete assembly="umbraco" type="userTasks" />

</tasks>

</nodeType>

<nodeType alias="inventoryProductCategory">

<header>Product Category</header>

<usercontrol>/create/simple.ascx</usercontrol>

<tasks>

<create assembly="umbraco" type="userTasks" />

<delete assembly="umbraco" type="userTasks" />

</tasks>

</nodeType>

<!-- END CUSTOM SECTION -->

Friday, October 29, 2010

Journey with Umbraco

 

This month, I start my journey with umbraco.
My first impression that this CMS framework is very cool.
You pretty much can do anything and extend whatever you need.

My objective before this end of this year is to build a very super supreme website with umbraco. Let see how it goes and hopefully I have enough time too.

-KK-

Saturday, December 29, 2007

Jesus answers all my prayers

Tonight, I couldn’t sleep before I archive this true story into my blogs. I just don’t want to waste these wonderful experiences. Hopefully, It can be a blessing for others as well.

Before 2004, I was confused about my life. I had a dream to become IT programmer and scientists on day, but it seemed impossible for me. I came from a broken home family. Even I am a Christian and every week going to church at least 3 times, my personality was still broken.

I always asked God, Why God gave me this family, not like another families. Every time I saw my friend’s father or families, I always cry to God. Every time the preacher talks about Father’s love, I always cry. I could not believe it because I never feel it.

After finishing my senior high school, I decide to continue studied at STTS which had a connection with Swinburne University. (2 Years in Indonesia and 1 Year in Australia). At that time, I thought that I could run away from my parents, work in Australia until getting PR (Permanent Resident) and live independent without my parents.

The reasons were that IT and technology in Australia is much better than in Indonesia.

At that time, it was so miraculous because my father allowed me to enter that university even later on my father always angry about this and asked my mother to pay as well and pay only 1 year.

November 2004, I arrived in Melbourne, Australia with uncertainties. I had known that to be eligible to get PR (Permanent Resident), I need to stay at least 2 years and get a master degree.

I can only depend on God alone. I believe in “Matthew 6:33 - seek first the kingdom of God, and his righteousness; and all these things shall be added unto you”.

No matter how busy I am, at any time, I make my God in the first place.

In order to eligible to get my PR (Permanent Resident), I have to work extremely hard.

In the beginning before I start my university until January 2005, I had become a Gardener once week + Leaflet delivery. I was tremendously hard, but I thought it was better to die in here rather I had to go back to Indonesia after finished my studying.

I had planned to save my living cost that my parents give me to continue my master degree ($10.000). So I need to get $6.000 more in a year + my living cost.

I also need to study hard as well, because if I failed my subject, I will not able to pay it or even to finish my bachelor degree.

Luckily on February 2005, God give me a better job as stock taker in Asian Grocery. It was very hard job and in order to get more money I need to work 12 hours and twice a week sometimes third times a week. But again It is better rather than I have to go back to my country.

After my first semester on 2005, God blessed me with a good score in my studies. I got HD (High Distinction) for all my subjects. I don’t know how, because officially I only study after 10 – 12 PM after works and the other days I have attended lectures, labs and working on my assignments. But I pays really attentions to the lecture and always come even I was sick.

Because of that good score, Sebastian Ng, My program manager gives me opportunities to become a Tutor and IT Helpdesk. In the first time my English was really horrible, but I don’t know how God can help me become Tutor in 3 subjects in 1 semester. Those subjects were new for me because I never took those subjects in Swinburne. So in order to be able to tutoring a university student which most of them are older than me, I need to do 3 more extra studying instead of my subjects that I took that semester. It was really miraculous.

Tutoring helps me to pay my living cost and saving more money for my master degree as well. It helps me to practice my English and my knowledge as well. However at that time, I still continue worked at Asian Grocery because I still need steady money. If tutoring, I did not get any money in holiday periods.

Some of my friends recommend me to find another job instead of Asian Grocery because the salary is very small.

I had try to apply IT job, But I still did not have permanent resident and no degree.

In certain point, I ever feel disappointed with my studies. Because It seems useless studying hard but only get this boring job.

However I believe at “Luke 16: 10 – be faithful in small things and God will trust you with bigger things”


Finally after finishing my bachelor, I have money for paying my master degree $16.000. It was really blessing & miraculous for me.

However, I have to save money for another living cost again for 1 year master while I am studying (at least $10.000) + Permanent Resident ($4.500).

Once again, I only depend on God. If God can make me came to Australia, finish my bachelor, Why God can not finish my master or even my Permanent Resident (PR)?

After 1 year worked in Asian Grocery, I have to find another position because they don’t have enough shifts anymore. However once again, God was good to me. On the same day February 2006, I directly get another job as Kitchen Hand on Italian Restaurant. I was very happy. The salary is higher but it is harder. I had to wash carpet at 12 pm and I went home at 1 in the morning. After worked I could not sleep, because all of my body was so sick.

After 1 month, I got rheumatic because of that job, however once more, God give me an opportunity to do more tutoring and interview with Microsoft and TCS. So I can not do kitchen hand anymore because I can not match my schedule.

On June 2006, I try to find a job which is related to my studies (IT). I get 3 opportunities to build software to them. Before they even pay me, I already started or even finished those projects. But for certain reason all of them are cheated me. 2 of them did not pay me anything. And 1 of them because of certain reason they only can pay me half price.

At that point I was really down. I could not do anything; even I have tried all of my power, energy, skill, time and knowledge. I realize people can work so hard but If God does not give a good opportunity, everything will be useless.

However not long after that, on August 2006, God answer my prayer by giving me a proper job at Computelec (Port Melbourne). This position is very easy and good salary but it is too far and there is no opportunity to be a full time after I graduate my master degree.

After 1 month worked in Computelec, God answers my prayer again, I get an opportunity as a software developer in good IT Company which is offer me full time position after I graduated. It is very close from my university and where I was living and it gave me a lot of experiences as well. I was really blessed to get this job before I graduate or even become full time before I get my Permanent Resident.

After graduating from Master Degree with full HD (High Distinction) mark, which again so miraculous for me, I get an offer to do a summer research at Swinburne for $ 4.000.


So on 15 of December 2006, I can use lawyer for applying my permanent resident ($4.000). That process was so miraculous so I got my permanent resident on 16 May 2007.

Now, I can live in Australia nicely and getting my dream with full hope and believe in Him.

When I looked back, I feel so graceful and proud of my God. I was nothing without Him. He always besides me and care for me even in the beginning, it seems He just watching my problems and gives me strength.

God has been answering all prayers and my questions.

I never feel disappointed anymore having my earthly father like this anymore. God has changed everything which seems unfair to me into a blessing. Moreover He always finds any excuse to bless me. (Gen 50:20 ... Ye thought evil against me, But God meant it unto good. ...)


Probably I may have a good earthly father, but I will never feel how good my Father in Heaven for me.

I also thanks to my parents who had been worked so hard to help me finish my study as well. All of supports from my mother and my father, I will never forget.

Hope my experience will help you who in problems or waiting for God’s answer. I know it is very hard. But at the end, you will definitely amaze. “HOW GREAT OUR GOD IS”