Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Events
Videos
Audiobooks
Packt Hub
Free Learning
Arrow right icon
timer SALE ENDS IN
0 Days
:
00 Hours
:
00 Minutes
:
00 Seconds

How-To Tutorials - CMS and E-Commerce

830 Articles
article-image-understanding-dotnetnuke-core-architecture
Packt
06 Apr 2010
13 min read
Save for later

Understanding the DotNetNuke Core Architecture

Packt
06 Apr 2010
13 min read
Architecture overview As opposed to traditional web applications that may rely on a multitude of web pages to deliver content, DotNetNuke uses a single main page called Default.aspx. The content for this page is generated dynamically by using a tabID value to retrieve the skin and modules needed to build the page requested, from the DotNetNuke database. Before we move on, we should discuss what is meant by a tab and a page. As you read this article, you will notice the word "tab" is sometimes used when referring to pages in your DotNetNuke portal. In the original IBuySpy application, pages were referred to as tabs because they resembled tabs when added to the page. IBuySpy application, the skeleton ASP.NET Framework, was created by Microsoft to demonstrate ASP.NET features, and DotNetNuke was originally derived from it. This continued in the original versions of the DotNetNuke project. Starting with version 3.0, and continuing with version 5.2.x, there has been an ongoing effort to rename most of these instances to reflect what they really are: pages. Most references to "tabs" have been changed to "pages", but the conversion is not complete. For this reason, you will see both—tabs and pages—in the database, in the project files, and in this text. We will use these terms interchangeably throughout this text as we look into the core architecture of DNN. We will begin with a general overview of what happens when a user requests a page on your DotNetNuke portal. The process for rendering a page in DotNetNuke works like this: a user navigates to a page on your portal; this calls the Default.aspx page, passing the tabid parameter in the querystring to let the application identify the page being requested. The example http://www.dotnetnuke.com/Default. aspx?tabid=476 demonstrates this. DotNetNuke 3.2 introduced something called URL rewriting. This takes the querystring shown above and rewrites it so that it is in a format that helps increase search engine hits. We will cover the HTTP module that is responsible for this in more detail later in this article. The rewritten URL would resemble http://localhost/DotNetNuke/Home.aspx. This assumes that the page name for tabid 476 is Home. While referring to URLs in this article we will be using the non-rewritten version of the URL. URL rewriting can be turned off in the friendly URL section of the Host Settings page. The querystring value (?tabid=476) is sent to the database, where the information required for the page is retrieved, as shown in the following diagram: The portal that the user is accessing can be determined in a number of ways, but as you can see from the Tabs table (see the following screenshot), each page/tab contains a reference to the portal it belongs to in the PortalID field. Once the server has a reference to the page that the user requested (using the tabID), it can determine what modules belong to that page. Although there are many more tables involved in this process, you can see that these tables hold not only the page and modules needed to generate the page, but also what pane to place them on (PaneName) and what container skin to apply (ContainerSrc). All of this information is returned to the web server, and the Default.aspx page is constructed with it and returned to the user who requested it along with the required modules and skins, as shown in the following diagram. Now, this is of course a very general overview of the process, but as we work through this article, we will delve deeper into the code that makes this process work, and in the end, show a request work its way through the framework to deliver a page to a user. Diving into the core There are over 160,000 lines of code in the DotNetNuke application. There is no practical (or even possible) way to cover the entire code base. In this section, we will go in depth into what I believe are the main portions of the code base: the PortalSettings as well as the companion classes found in the portals folder; the web.config file including the HTTP Modules and Providers; and the Global.asax and Globals.vb files. We will start our discussion of the core with two objects that play an integral part in the construction of the architecture. The Context object and the PortalSettings class will both be referred to quite often in the code, and so it is important that you have a good understanding of what they do. Using the Context object in your application ASP .NET has taken intrinsic objects like the Request and the Application objects and wrapped them together with other relevant items into an intrinsic object called Context. The Context object (HttpContext) can be found in the System.Web namespace. In the following table, you will find some of the objects that make up the HttpContext object. Title Description Application Gets the HttpApplicationState object for the current HTTP request. Cache Gets the Cache object for the current HTTP request. Current Gets the HttpContext object for the current HTTP request. This is a static (shared in VB) property of the HttpContext class, through which you access all other instance properties discussed in this table, that together enable you to process and respond to an HTTP request. Items Gets a key-value collection that can be used to organize and share data between an IHttpModule and an IHttpHandler during an HTTP request. Request Gets the HttpRequest object for the current HTTP request. This is used to extract data submitted by the client, and information about the client itself (for example, IP ), and the current request. Response Gets the HttpResponse object for the current HTTP response. This is used to send data to the client together with other response-related information such as headers, cacheability, redirect information, and so on. Server Gets the HttpServerUtility object that provides methods used in processing web requests. Session Gets the HttpSessionState instance for the current HTTP request. User Gets or sets security information for the current HTTP request. Notice that most of the descriptions talk about the "current" request object, or the "current" response object. The Global.asax file, which we will look at soon, reacts on every single request made to your application, and so it is only concerned with whoever is "currently" accessing a resource. The HttpContext object contains all HTTP-specific information about an individual HTTP request. In particular, the HttpContext.Current property can give you the context for the current request from anywhere in the application domain. The DotNetNuke core relies on the HttpContext.Current property to hold everything from the application name to the portal settings and through this makes it available to you. The PortalSettings class The portal settings play a major role in the dynamic generation of your pages and as such will be referred to quite often in the other portions of the code. The portal settings are represented by the PortalSettings class which you will find in the EntitiesPortalPortalSettings.vb file. As you can see from the private variables in this class, most of what goes on in your portal will at some point needto access this object. This object will hold everything from the ID of the portal to the default language, and as we will see later, is responsible for determining the skins and modules needed for each page. Private _PortalId As IntegerPrivate _PortalName As StringPrivate _HomeDirectory As StringPrivate _LogoFile As StringPrivate _FooterText As StringPrivate _ExpiryDate As DatePrivate _UserRegistration As IntegerPrivate _BannerAdvertising As IntegerPrivate _Currency As StringPrivate _AdministratorId As IntegerPrivate _Email As StringPrivate _HostFee As SinglePrivate _HostSpace As IntegerPrivate _PageQuota As IntegerPrivate _UserQuota As IntegerPrivate _AdministratorRoleId As IntegerPrivate _AdministratorRoleName As StringPrivate _RegisteredRoleId As IntegerPrivate _RegisteredRoleName As StringPrivate _Description As StringPrivate _KeyWords As StringPrivate _BackgroundFile As StringPrivate _GUID As GuidPrivate _SiteLogHistory As IntegerPrivate _AdminTabId As IntegerPrivate _SuperTabId As IntegerPrivate _SplashTabId As IntegerPrivate _HomeTabId As IntegerPrivate _LoginTabId As IntegerPrivate _UserTabId As IntegerPrivate _DefaultLanguage As StringPrivate _TimeZoneOffset As IntegerPrivate _Version As StringPrivate _ActiveTab As TabInfoPrivate _PortalAlias As PortalAliasInfoPrivate _AdminContainer As SkinInfoPrivate _AdminSkin As SkinInfoPrivate _PortalContainer As SkinInfoPrivate _PortalSkin As SkinInfoPrivate _Users As IntegerPrivate _Pages As Integer The PortalSettings class itself is simple. It is filled by using one of the constructors that accepts one or more parameters. These constructors then call the private GetPortalSettings method . The method is passed a tabID and a PortalInfo object. You already know that the tabID represents the ID of the page being requested, but the PortalInfo object is something new. This class can be found in the same folder as the PortalSettings class and contains the basic information about a portal such as PortalID, PortalName, and Description. However, from the PortalSettings object, we can retrieve all the information associated with the portal. If you look at the code inside the constructors, you will see that the PortalController object is used to retrieve the PortalInfo object. The PortalInfo object is saved in cache for the time that is specifi ed on the Host Settings page. A drop-down box on the Host Settings page (DesktopModulesAdminHostSettingsHostSettings.ascx) is used to set the cache. No caching:0 Light caching:1 Moderate caching:3 Heavy caching:6 The value in this dropdown ranges from 0 to 6; the code in the DataCache object takes the value set in the drop-down and multiplies it by 20 to determine the cache duration. Once the cache time is set, the method checks if the PortalSettings object already resides there. Retrieving these settings from the database for every request would cause your site to run slowly, so placing them in a cache for the duration you select helps increase the speed of your site. Recent versions of DotNetNuke have focused heavily on providing an extensive caching service. An example of this can be seen in the following code: Dim cacheKey As String = String.Format(DataCache.PortalCacheKey,PortalId.ToString())Return CBO.GetCachedObject(Of PortalInfo)(New CacheItemArgs(cacheKey, DataCache.PortalCacheTimeOut,DataCache.PortalCachePriority, PortalId),AddressOf GetPortalCallback) We can see in the previous code that the CBO object is used to return an object from the cache. CBO is an object that is seen frequently throughout the DotNetNuke core. This object's primary function is to return the populated business object. This is done in several ways using different methods provided by CBO. Some methods are used to map data from an IDataReader to the properties of a business object. However, in this example, the Get CachedObject method handles the logic needed to determine if the object should be retrieved from the cache or from the database. If the object is not already cached, it will use the GetPortalCallback method passed to the method to retrieve the portal settings from the database. This method is located in the PortalController class (EntitiesPortalPortalController.vb) and is responsible for retrieving the portal information from the database. Dim portalID As Integer = DirectCast(cacheItemArgs.ParamList(0),Integer)Return CBO.FillObject(Of PortalInfo)(DataProvider.Instance _.GetPortal(portalID, Entities.Host.Host.ContentLocale.ToString)) This will fi ll the PortalInfo object (EntitiesPortalPortalInfo.vb), which as we mentioned, holds the portal information. This object in turn is returned to the GetCachedObject method. Once this is complete, the object is then cached to help prevent the need to call the database during the next request for the portal information. There is also a section of code (not shown) that verifi es whether the object was successfully stored in the cache and adds an entry to the event log if the item failed to be cached. ' if we retrieved a valid object and we are using cachingIf objObject IsNot Nothing AndAlso timeOut > 0 Then' save the object in the cacheDataCache.SetCache(cacheItemArgs.CacheKey, objObject, _cacheItemArgs.CacheDependency, Cache.NoAbsoluteExpiration, _TimeSpan.FromMinutes(timeOut), cacheItemArgs.CachePriority, _cacheItemArgs.CacheCallback)…End If After the portal settings are saved, the properties of the current tab information are retrieved and populated in the ActiveTab property. The current tab is retrieved by using the tabID that was originally passed to the constructor. This is handled by the VerifyPortalTab method and done by getting a list of all of the tabs for the current portal. Like the portal settings themselves, the tabs are saved in cache to boost performance. The calls to the caching provider, this time, are handled by the TabController (EntitiesTabsTabController.vb). In the last VerifyPortalTab method, the code will loop through all of the host and non-host tabs, returned by the TabController, for the site until the current tab is located. ' find the tab in the portalTabs collectionIf TabId <> Null.NullInteger ThenIf portalTabs.TryGetValue(TabId, objTab) Then'Check if Tab has been deleted (is in recycle bin)If Not (objTab.IsDeleted) ThenMe.ActiveTab = objTab.Clone()isVerified = TrueEnd IfEnd IfEnd If' find the tab in the hostTabs collectionIf Not isVerified AndAlso TabId <> Null.NullInteger ThenIf hostTabs.TryGetValue(TabId, objTab) Then'Check if Tab has been deleted (is in recycle bin)If Not (objTab.IsDeleted) ThenMe.ActiveTab = objTab.Clone()isVerified = TrueEnd IfEnd IfEnd If If the tab was not found in either of these collections, the code attempts to use the splash page, home page, or the fi rst page of the non-host pages. After the current tab is located, further handling of some of its properties is done back in the GetPortalSettings method. This includes formatting the path for the skin and default container used by the page, as well as collecting information on the modules placed on the page. Me.ActiveTab.SkinSrc = _SkinController.FormatSkinSrc(Me.ActiveTab.SkinSrc, Me)Me.ActiveTab.SkinPath = _SkinController.FormatSkinPath(Me.ActiveTab.SkinSrc)…For Each kvp As KeyValuePair(Of Integer, ModuleInfo) In _objModules.GetTabModules(Me.ActiveTab.TabID)' clone the module object _( to avoid creating an object reference to the data cache )Dim cloneModule As ModuleInfo = kvp.Value.Clone' set custom propertiesIf Null.IsNull(cloneModule.StartDate) ThencloneModule.StartDate = Date.MinValueEnd IfIf Null.IsNull(cloneModule.EndDate) ThencloneModule.EndDate = Date.MaxValueEnd If' containerIf cloneModule.ContainerSrc = "" ThencloneModule.ContainerSrc = Me.ActiveTab.ContainerSrcEnd IfcloneModule.ContainerSrc = _SkinController.FormatSkinSrc(cloneModule.ContainerSrc, Me)cloneModule.ContainerPath = _SkinController.FormatSkinPath(cloneModule.ContainerSrc)' process tab panesIf objPaneModules.ContainsKey(cloneModule.PaneName) = False ThenobjPaneModules.Add(cloneModule.PaneName, 0)End IfcloneModule.PaneModuleCount = 0If Not cloneModule.IsDeleted ThenobjPaneModules(cloneModule.PaneName) = _objPaneModules(cloneModule.PaneName) + 1cloneModule.PaneModuleIndex = _objPaneModules(cloneModule.PaneName) - 1End IfMe.ActiveTab.Modules.Add(cloneModule)Next We have now discussed some of the highlights of the PortalSettings object as well as how it is populated with the information it contains. In doing so, we also saw abrief example of the robust caching service provided by DotNetNuke. You will see the PortalSettings class referenced many times in the core DotNetNuke code, so gaining a good understanding of how this class works will help you to become more familiar with the DNN code base. You will also fi nd this object to be very helpful while building custom extensions for DotNetNuke. The caching provider itself is a large topic, and reaches beyond the scope of this article. However, simply understanding how to work with it in the ways shown in these examples should satisfy the needs of most developers. It is important to note that, you can get any type of object cached by DNN by passing in a key for your object to DataCache.SetCache method, together with the data, and some optional arguments. While fetching the object back from DataCache.GetCache, you pass in the same key, and check the result. A non-null (non-Nothing in VB) return value means you have fetched the object successfully from the cache, otherwise you would need to fetch it from the database.
Read more
  • 0
  • 0
  • 4138

article-image-aspnet-and-jquery-how-to-create-rich-content
Packt
27 Apr 2011
7 min read
Save for later

ASP.NET and jQuery: how to create rich content

Packt
27 Apr 2011
7 min read
In a nutshell, the jQuery UI library provides the following: Completely configurable widgets like accordion, tabs, progressbar, datepicker, slider, autocomplete, dialog, and button Interactive features like draggable, droppable, resizable, selectable, and sortable Advanced animation effects like show, hide, toggle, blind, bounce, explode, fade, highlight, pulsate, puff, scale, slide, etc Customisable themes to suit the look and feel of your website Using jQuery and ASP.NET together In this article, we will primarily take a look at integration of jQuery UI with ASP.NET to build rich content quickly and easily. Read more: ASP.NET: Using jQuery UI Widgets Getting started Let's start by creating a new ASP.NET website Chapter9 in Visual Studio. Go to the download page of jQuery UI at http://jqueryui.com/download, which allows customizable downloads based on the features required in the web application. For the purpose of this article, we will download the default build as shown next: (Move the mouse over the image to enlarge.) jQuery UI allows various custom themes. We will select the Sunny theme for our project: Save the downloaded file. The download basically consists of the following: css folder consisting of the the theme files development-bundle folder consisting of demos, documents, raw script files, etc. js folder consisting of the minified version of jQuery library and jQuery UI Save the earlier mentioned css folder in the current project. Save the minified version of jQuery UI and jQuery library in a script folder js in the project. In addition to including the jQuery library on ASP.NET pages, also include the UI library as shown: <script src="js/jquery-1.4.1.js" type="text/javascript"></script> <script src="js/jquery-ui-1.8.9.custom.min.js" type="text/javascript"></script> Include the downloaded theme on the aspx pages as follows: <link href="css/sunny/jquery-ui-1.8.9.custom.css" rel="stylesheet" type="text/css" /> Now let's move on to the recipes and explore some of the powerful functionalities of jQuery UI. Creating an accordion control The jQuery accordion widget allows the creation of collapsible panels on the page without the need for page refresh. Using an accordion control, a single panel is displayed at a time while the remaining panels are hidden. Getting Ready Create a new web form Recipe1.aspx in the current project. Add a main content panel to the page. Within the main panel, add pairs of headers and subpanels as shown next: <asp:Panel id="contentArea" runat="server"> <h3><a href="#">Section 1</a></h3> <asp:Panel ID="Panel1" runat="server"> Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. </asp:Panel> <h3><a href="#">Section 2</a></h3> <asp:Panel ID="Panel2" runat="server"> Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. </asp:Panel> <h3><a href="#">Section 3</a></h3> <asp:Panel ID="Panel3" runat="server"> Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. </asp:Panel> <h3><a href="#">Section 4</a></h3> <asp:Panel ID="Panel4" runat="server"> Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum </asp:Panel> </asp:Panel> Add some styling to the main content panel as required: #contentArea { width: 300px; height: 100%; } Our accordion markup is now ready. We will now transform this markup into an accordion control using the functionalities of jQuery UI. How to do it... In the document.ready() function of the jQuery script block, apply the accordion() method to the main content panel: $("#contentArea").accordion(); Thus, the complete jQuery UI solution for the problem at hand is as follows: <script language="javascript" type="text/javascript"> $(document).ready(function(){ $("#contentArea").accordion(); }); </script> How it works... Run the web page. The accordion control is displayed as shown in the following screenshot: Click on the respective panel headers to display the required panels. Note that the accordion control only displays the active panel at a time. The remaining panels are hidden from the user. There's more... For detailed documentation on the jQuery UI accordion widget, please visit http://jqueryui.com/demos/accordion/. Creating a tab control The jQuery UI tab widget helps to create tab controls quickly and easily on ASP.NET web pages. The tab control helps in organizing content on a page thus improving the presentation of bulky content. With the help of jQuery UI tab widget, the content can also be retrieved dynamically using AJAX. In this recipe, we will see a simple example of applying this powerful widget to ASP.NET forms. Getting Ready Create a new web form Recipe2.aspx in the current project. Add an ASP.NET container panel to the page. Within this container panel, add subpanels corresponding to the tab contents. Also add hyperlinks to each of the subpanels. Thus the complete aspx markup of the web form is as shown next: <form id="form1" runat="server"> <asp:panel id="contentArea" runat="server"> <ul> <li><a href="#tab1">Tab 1</a></li> <li><a href="#tab2">Tab 2</a></li> <li><a href="#tab3">Tab 3</a></li> <li><a href="#tab4">Tab 4</a></li> </ul> <asp:panel ID="tab1" runat="server"> <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p> </asp:panel> <asp:panel ID="tab2" runat="server"> <p>Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p> </asp:panel> <asp:panel ID="tab3" runat="server"> <p>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. </p> </asp:panel> <asp:panel ID="tab4" runat="server"> <p>Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum</p> </asp:panel> </asp:panel> </form> Next, we will see how we can transform this markup into a tab control using jQuery UI. How to do it... In the document.ready() function of the jQuery script block, apply the tabs() method to the container panel as follows: $("#contentArea").tabs(); Thus, the complete jQuery UI solution for creating the tab control is as follows: <script language="javascript" type="text/javascript" $(document).ready(function(){ $("#contentArea").tabs(); }); </script> How it works... Run the web form. The page displays the tabbed content as follows: Click on the respective tab headers to view the required content. There's more… For detailed documentation on the jQuery UI tabs widget, visit the jQuery website.
Read more
  • 0
  • 0
  • 4133

article-image-cocoa-and-objective-c-animating-calayers
Packt
27 May 2011
6 min read
Save for later

Cocoa and Objective-C: Animating CALayers

Packt
27 May 2011
6 min read
Cocoa and Objective-C Cookbook Understanding the CALayer class In this recipe, we will use multiple layers to draw our custom view. Using a delegate method, we will draw the background of our view. A second layer will be used to include an image centered in the view. When complete, the sample application will resemble the following screenshot: Getting ready In Xcode, create a new Cocoa Application and name it CALayer. How to do it... In the Xcode project, right-click on the Classes folder and choose Add…, then choose New File… Under the MacOS X section, select Cocoa Class, then select Objective-C class. Finally, choose NSView in the Subclass of popup. Name the new file MyView.m. In the Xcode project, expand the Frameworks group and then expand Other Frameworks. Right-click on Other Frameworks and choose Add…, then choose Existing Frameworks… Find QuartzCore.framework in the list of frameworks and choose Add. Click on MyView.m to open it and add the following import: #import <QuartzCore/QuartzCore.h> Remove the initWithFrame: and drawRect: methods. Add the following awakeFromNib method: - (void) awakeFromNib { CALayer *largeLayer = [CALayer layer]; [largeLayer setName:@"large"]; [largeLayer setDelegate:self]; [largeLayer setBounds:[self bounds]]; [largeLayer setBorderWidth:4.0]; [largeLayer setLayoutManager:[CAConstraintLayoutManager layoutManager]]; CALayer *smallLayer = [CALayer layer]; [smallLayer setName:@"small"]; CGImageRef image = [self convertImage:[NSImage imageNamed:@"LearningJQuery"]]; [smallLayer setBounds:CGRectMake(0, 0, CGImageGetWidth(image), CGImageGetHeight(image))]; [smallLayer setContents:(id)image]; [smallLayer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMidY relativeTo:@"superlayer" attribute:kCAConstraintMidY]]; [smallLayer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMidX relativeTo:@"superlayer" attribute:kCAConstraintMidX]]; CFRelease(image); [largeLayer addSublayer:smallLayer]; [largeLayer setNeedsDisplay]; [self setLayer:largeLayer]; [self setWantsLayer:YES]; } Add the following two methods to the MyView class as well: - (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)context { CGContextSetRGBFillColor(context, .5, .5, .5, 1); CGContextFillRect(context, [layer bounds]); } - (CGImageRef) convertImage:(NSImage *)image { CGImageSourceRef source = CGImageSourceCreateWithData((CFDataRef )[image TIFFRepresentation], NULL); CGImageRef imageRef = CGImageSourceCreateImageAtIndex(source, 0, NULL); CFRelease(source); return imageRef; } Open the MyView.h file and add the following method declaration: - (CGImageRef) convertImage:(NSImage *)image; Right-click on the CALayer project in Xcode's project view and choose Add…, then choose Existing Files…. Choose the LearningJQuery.jpg image and click on Add. Double-click on the MainMenu.xib file in the Xcode project. From Interface Builder's Library palette, drag a Custom View into the application window. From Interface Builder's Inspectors palette, select the Identity tab and set the Class popup to MyView. Back in Xcode, choose Build and Run from the toolbar to run the application. How it works... We are creating two layers for our view. The first layer is a large layer, which will be the same size as our MyView view. We set the large layers delegate to self so that we can draw the layer in the drawLayer: delegate method. The drawLayer: delegate method simply fills the layer with a mid-gray color. Next, we set the bounds property and a border width property on the larger layer. Next, we create a smaller layer whose contents will be the image that we included in the project. We also add a layout manager to this layer and configure the constraints of the layout manager to keep the smaller layer centered both horizontally and vertically relative to the larger view using the superlayer keyword. Lastly, we set the small layer as a sub-layer of the large layer and force a redraw of the large layer by calling setNeedsDisplay. Next, we set the large layer as the MyView's layer. We also need to call the setWantsLayer:YES on the MyView to enable the use of layers in our view. There's more... Since we used a layout manager to center the image in the view, the layout manager will also handle the centering of the image when the user resizes the view or window. To see this in action, modify the Size properties in Interface Builder for the custom view as shown in the screenshot below: Animation by changing properties Cocoa provides a way to animate views by changing properties using implied animations. In this recipe, we will resize our custom view when the resize button is clicked, by changing the views frame size. Getting ready In Xcode, create a new Cocoa Application and name it ChangingProperties. How to do it... In the Xcode project, right-click on the Classes folder and choose Add…, then choose New File… Under the MacOS X section, select Cocoa Class, then select Objective-C class. Finally, choose NSView from the Subclass of popup. Name the new file MyView.m. Click on the MyView.m file to open it and add the following in the drawRect: method: NSBezierPath *path = [NSBezierPath bezierPathWithRoundedRect:[ self bounds] xRadius:8.0 yRadius:8.0]; [path setClip]; [[NSColor whiteColor] setFill]; [NSBezierPath fillRect:[self bounds]]; [path setLineWidth:3.0]; [[NSColor grayColor] setStroke]; [path stroke]; Click on the ChangingPropertiesAppDelegate.h to open it. Next, insert an import for the MyView.h header file: #import "MyView.h" Add the following variables to the class interface: NSButton *button; MyView *myView; Add the following properties to the class interface: @property (assign) IBOutlet NSButton *button; @property (assign) IBOutlet MyView *myView; Add the method declaration for when the Resize button is clicked: - (IBAction) resizeButtonHit:(id)sender; Click on the ChangingPropertiesAppDelegate.m file to open it. Add our synthesized variables below the synthesized window variable: @synthesize button; @synthesize myView; Create a global static boolean for tracking the size of the view: static BOOL isSmall = YES; Add the resizeButtonHit: method to the class implementation: - (IBAction) resizeButtonHit:(id)sender { NSRect small = NSMakeRect(20, 250, 150, 90); NSRect large = NSMakeRect(20, 100, 440, 240); if (isSmall == YES) { [[myView animator] setFrame:large]; isSmall = NO; } else { [[myView animator] setFrame:small]; isSmall = YES; } } Double-click on the MainMenu.xib file in the Xcode project. From Interface Builders Library palette, drag a Custom View into the application window. From Interface Builder's Inspector's palette, select the Identity tab and set the Class popup to MyView. From the Library palette, drag a Push Button into the application window. Adjust the layout of the Custom View and button so that it resembles the screenshot below: From Interface Builder's Inspector's palette, select the Identity tab and set the Class popup to MyView. Right-click on the Changing Properties App Delegate so that you can connect the outlets to the MyView Custom View, the Resize Push Button, and the resizeButtonHit action: Back in Xcode, choose Build and Run from the toolbar to run the application. How it works... We define two sizes for our view, one small size that is the same as the initial size of the view, and one large size. Depending on the state of the isSmall global variable, we set the view's frame size to one of our predefined sizes. Note that we set the view's frame via the views animator property. By using this property, we make use of the implicit animations available in the view. There's more... Using the same technique, we can animate several other properties of the view such as its position or opacity. For more information on which properties can be implicitly animated, see Apple's Core Animation Programming Guide.
Read more
  • 0
  • 0
  • 4130

article-image-customizing-your-vim-work-area
Packt
14 May 2010
12 min read
Save for later

Customizing Your Vim for work area

Packt
14 May 2010
12 min read
(Read more interesting articles on Hacking Vim 7.2 here.) Work area personalization In this article, we introduce a list of smaller, good-to-know modifications for the editor area in Vim. The idea with these recipes is that they all give you some sort of help or optimization when you use Vim for editing text or code. Adding a more visual cursor Sometimes, you have a lot of syntax coloring in the file you are editing. This can make the task of tracking the cursor really hard. If you could just mark the line the cursor is currently in, then it would be easier to track it. Many have tried to fix this with Vim scripts, but the results have been near useless (mainly due to slowness, which prevented scrolling longer texts at an acceptable speed). Not until version 7 did Vim have a solution for this. But then it came up with not just one, but two possible solutions for cursor tracking. The first one is the cursorline command , which basically marks the entire line with, for example, another background color without breaking the syntax coloring. To turn it on, use the following command: :set cursorline The color it uses is the one defined in the CursorLine color group. You can change this to any color or styling you like, for example: :highlight CursorLine guibg=lightblue ctermbg=lightgray If you are working with a lot of aligned file content (such as tab-separated data), the next solution for cursor tracking comes in handy: :set cursorcolumn This command marks the current column (here the cursor is placed) by coloring the entire column through the entire file, for example. As with the cursor line, you can change the settings for how the cursor column should be marked. The color group to change is named cursorcolumn. Adding both the cursor line and column marking makes the cursor look like a crosshair, thus making it impossible to miss. Even though the cursorline and cursorcolumn functionalities are implemented natively in Vim, it can still give quite a slowdown when scrolling through the file. Adding line numbers Often when compiling and debugging code, you will get error messages stating that the error is in some line. One could, of course, start counting lines from the top to find the line, but Vim has a solution to go directly to some line number. Just execute :XXX where XXX is the line number, and you will be taken to the XXX line. Alternatively, you can go into normal mode (press the Esc key) and then simply use XXXgg or XXXG (again XXX is the line number). Sometimes, however, it is nice to have an indication of the line number right there in the editor, and that's where the following command comes in handy: :set number Now, you get line numbers to the left of each line in the file. By default, the numbers take up four columns of space, three for numbers, and one for spacing. This meansthat the width of the numbers will be the same until you have more than 999 lines. If you get above this number of lines, an extra column will be added and the content will be moved to the right. Of course, you can change the default number of columns used for the line numbers. This can be achieved by changing the following property: :set numberwidth=XXX Replace XXX with the number of columns that you want. Even though it would be nice to make the number of columns higher in order to get more spacing between code and line numbers, this is not achievable with the numberwidth property. This is because the line numbers will be right-aligned within the columns. In the following figure, you can see how line numbers are shown as right-aligne when a higher number of columns are set in numberwidth: You can change the styling of the line numbers and the columns they are in by making changes to the LineNr color group. Spell checking your language We all know it! Even if we are really good spellers, it still happens from time to time that we misspell a word or hit the wrong keys. In the past, you had to run your texts (that you had written in Vim) through some sort of spell checker such as Aspell or Ispell . This was a tiresome process that could only be performed as a final task, unless you wanted to do it over and over again. With version 7 of Vim, this troublesome way of spell checking is over. Now, Vim has got a built-in spell checker with support for more than 50 languages from around the world. The new spell checker marks the wrongly written words as you type them in, so you know right away that there is an error. The command to execute to turn on this helpful spell checker feature is: :set spell This turns on the spell checker with the default language (English). If you don't use English much and would prefer to use another language in the spell checker, then there is no problem changing this. Just add the code of the language you would like to use to the spelllang property . For example: :set spelllang=de Here, the language is set to German (Deutsch) as the spell checker language of choice. The language name can be written in several different formats. American English, for example, can be written as: en_us us American Names can even be an industry-related name such as medical. If Vim does not recognize the language name, Vim will highlight it when you execute the property-setting command. If you change the spelllang setting to a language not already installed, then Vim will ask you if it should try to automatically retrieve it from the Vim homepage. Personally, I tend to work in several different languages in Vim, and I really don't want to tell Vim all the time which language I am using right now. Vim has a solution for this. By appending more language codes to the spelllang property (separated by commas), you can tell Vim to check the spelling in more than one language. :set spelllang=en,da,de,it Vim will then take the languages from the start to the end, and check if the words match any word in one of these languages. If they do, then they are not marked as a spelling error. Of course, this means that you can have a word spelled wrong in the language you are using but spelled correctly in another language, thereby introducing a hidden spelling error. You can find language packages for a lot of languages at the Vim FTP site: ftp://ftp.vim.org/pub/vim/runtime/spell. Spelling errors are marked differently in Vim and Gvim. In regular Vim, the misspelled word is marked with the SpellBad color group (normally, white on red). In Gvim, the misspelled word is marked with a red curvy line underneath the word. This can, of course, be changed by changing the settings of the color group. Whenever you encounter a misspelled word, you can ask Vim to suggest better ways to spell the word. This is simply done by placing the cursor over the word, going into the normal mode (press Esc), and then pressing Z + =. If possible, Vim will give you a list of good guesses for the word you were actually trying to write. In front of each suggestion is a number. Press the number you find in front of the right spelling (of the word you wanted) or press Enter if the word is not there. Often Vim gives you a long list of alternatives for your misspelled word, but unless you have spelled the word completely wrong, chances are that the correct word is within the top five of the alternatives. If this is the case, and you don't want to look through the entire list of alternatives, then you can limit the output with the following command: :set spellsuggest=X Set X to the number of alternative ways of spelling you want Vim to suggest. Adding helpful tool tips In the Modifying tabs recipe, we learned about how to use tool tips to store more information using less space in the tabs in Gvim. To build on top of that same idea with this recipe, we move on and use tool tips in other places in the editor. The editing area is the largest part of Vim. So, why not try to add some extra information to the contents of this area by using tool tips? In Vim, tool tips for the editing area are called balloons and they are only shown when the cursor is hovering over one of the characters. The commands you will need to know in order to use the balloons are: The first command is the one you will use to actually turn on this functionality in Vim. :set ballooneval The second command tells Vim how long it should wait before showing the tool tip/balloon (the delay is in milliseconds and as a default is set to 600). :set balloondelay=400 The last command is the one that actually sets the string that Vim will show in the balloon. :set ballonexpr="textstring" This can either be a static text string or the return of some function. In order to have access to information about the place where you are hovering over a character in the editor, Vim provides access to a list of variables holding such information: abc<space>and abc<enter> Both expand 123abc<space> Will not expand as the abbreviation is part of a word abcd<space> Will not expand because there are letters after the abbreviation abc Will not expand until another special letter is pressed So with these variables in hand, let's look at some examples. Example 1: The first example is based on one from the Vim help system. It shows how to make a simple function that will show the information from all the available variables. function! SimpleBalloon() return 'Cursor is at line/column: ' . v:beval_lnum . '/' . v:beval_col . ' in file ' . bufname(v:beval_bufnr) . '. Word under cursor is: "' . v:beval_text . '"' endfunction set balloonexpr=SimpleBalloon() set balloonevalcode 59 The result will look similar to the following screenshot: Example 2: Let's look at a more advanced example that explores the use of balloons for specific areas in editing. In this example, we will put together a function that gives us great information balloons for two areas at the same time: Misspelled words: The balloon gives ideas for alternative words Folded text: The balloon gives a preview of what's in the fold So, let's take a look at what the function should look for, to detect if the cursor is hovering over either a misspelled word or a fold line (a single line representing multiple lines folded behind it). In order to detect if a word is misspelled, the spell check would need to be turned on: :set spell If it is on, then calling the built-in spell checker function—spellsuggest()—would return alternative words if the hovered word was misspelled. So, to see if a word is misspelled, just check if the spellsuggest() returns anything. There is, however, a small catch. spellsuggest() also returns alternative, similar words if the word is not misspelled. To get around this, another function has to be used on the input word before putting it into the spellsuggest() function . This extra function is the spellbadword(). This basically moves the cursor to the first misspelled word in the sentence that it gets as input, and then returns the word. We just input a single word and if it is not misspelled, then the function cannot return any words. Putting no word into spellsuggest() results in getting nothing back, so we can now check if a word is misspelled or not. It is even simpler to check if a word is in a line, in a fold. You simply have to call the foldclosed()function on the line number of the line over which the cursor is hovering (remember v:beval_lnum ?), and it will return the number of the first line in the current fold; if not in a fold, then it returns -1. In other words, if foldclosed(v:beval_lnum) returns anything but -1 and 0, we are in a fold. Putting all of this detection together and adding functionality to construct the balloon text ends up as the following function: function! FoldSpellBalloon() let foldStart = foldclosed(v:beval_lnum ) let foldEnd = foldclosedend(v:beval_lnum) let lines = [] " Detect if we are in a fold if foldStart < 0 " Detect if we are on a misspelled word let lines = spellsuggest( spellbadword(v:beval_text)[ 0 ], 5, 0 ) else " we are in a fold let numLines = foldEnd - foldStart + 1 " if we have too many lines in fold, show only the first 14 " and the last 14 lines if ( numLines > 31 ) let lines = getline( foldStart, foldStart + 14 ) let lines += [ '-- Snipped ' . ( numLines - 30 ) . ' lines --' ] let lines += getline( foldEnd - 14, foldEnd ) else "less than 30 lines, lets show all of them let lines = getline( foldStart, foldEnd ) endif endif " return result return join( lines, has( "balloon_multiline" ) ? "n" : " " ) endfunction set balloonexpr=FoldSpellBalloon() set ballooneval The result is some really helpful balloons in the editing area of Vim that can improve your work cycle tremendously. The following screenshot shows how the information balloon could look when it is used to preview a folded range of lines from a file: Instead, if the balloon is used on a misspelled word, it will look like the following screenshot: In Production Boosters, you can learn more about how to use folding of lines to boost productivity in Vim.
Read more
  • 0
  • 0
  • 4103

article-image-ibm-filenet-p8-content-manager-administrative-tools-and-tasks
Packt
10 Feb 2011
11 min read
Save for later

IBM FileNet P8 Content Manager: Administrative Tools and Tasks

Packt
10 Feb 2011
11 min read
Getting Started with IBM FileNet P8 Content Manager Install, customize, and administer the powerful FileNet Enterprise Content Management platform Quickly get up to speed on all significant features and the major components of IBM FileNet P8 Content Manager Provides technical details that are valuable both for beginners and experienced Content Management professionals alike, without repeating product reference documentation Gives a big picture description of Enterprise Content Management and related IT areas to set the context for Content Manager Written by an IBM employee, Bill Carpenter, who has extensive experience in Content Manager product development, this book gives practical tips and notes with a step-by-step approach to design real Enterprise Content Management solutions to solve your business needs        The following will be covered in the next article. A discussion of an Object Store and what's in it An example of creating a custom class and adding custom properties to it FEM must run on a Microsoft Windows machine. Even if you are using virtual machine images or other isolated servers for your CM environment, you might wish to install FEM on a normal Windows desktop machine for your own convenience. Domain and GCD Here's a simple question: what is a P8 Domain? It's easy to give a simple answer—it's the top-level container of all P8 things in a given installation. That needs a little clarification, though, because it seems a little circular; things are in a Domain because a Domain knows about them. In a straightforward technical sense, things are in the same Domain if they share the same Global Configuration Database (GCD) . The GCD is, literally, a database. If we were installing additional CE servers, they would share that GCD if we wanted them to be part of the same Domain. When you first open FEM and look at the tree view in the left-hand panel, most of the things you are looking at are things at the Domain level. We'll be referring to the FEM tree view often, and we're talking about the left-hand part of the user interface, as seen in the following screenshot: FEM remembers the state of the tree view from session to session. When you start FEM the next time, it will try to open the nodes you had open when you exited. That will often mean something of a delay as it reads extensive data for each open Object Store node. You might find it a useful habit to close up all of the nodes before you exit FEM. Most things within a Domain know about and can connect directly to each other, and nothing in a given Domain knows about any other Domain. The GCD, and thus the Domain, contains: Simple properties of the Domain object itself Domain-level objects Configuration objects for more complex aspects of the Domain environment Pointers to other components, both as part of the CE environment and external to it It's a little bit subjective as to which things are objects and which are pointers to other components. It's also a little bit subjective as to what a configuration object is for something and what a set of properties is of that something. Let's not dwell on those philosophical subtleties. Let's instead look at a more specific list: Properties: These simple properties control the behavior of or describe characteristics of the Domain itself. Name and ID: Like most P8 objects, a Domain has both a Name and an ID. It's counterintuitive, but you will rarely need to know these, and you might even sometimes forget the name of your own Domain. The reason is that you will always be connecting to some particular CE server, and that CE server is a member of exactly one Domain. Therefore, all of the APIs related to a Domain object are able to use a defaulting mechanism that means "the current Domain". Database schemas: There are properties containing the database schemas for an Object Store for each type of database supported by P8. CM uses this schema, which is an actual script of SQL statements, by default when first fleshing out a new Object Store to create tables and columns. Interestingly, you can customize the schema when you perform the Object Store creation task (either via FEM or via the API), but you should not do so on a whim. Permissions: The Domain object itself is subject to access controls, and so it has a Permissions property. The actual set of access rights available is specific to Domain operations, but it is conceptually similar to access control on other objects. Domain-level objects: A few types of objects are contained directly within the Domain itself. We'll talk about configuration objects in a minute, but there are a couple of non-configuration objects in the Domain. AddOns: An AddOn is a bundle of metadata representing the needs of a discrete piece of functionality that is not built into the CE server. Some are provided with the product, and others are provided by third parties. An AddOn must first be created, and it is then available in the GCD for possible installation in one or more Object Stores. Marking Sets: Marking Sets are a Mandatory Access Control mechanism, Security Features and Planning. Individual markings can be applied to objects in an Object Store, but the overall definition resides directly under Domain so that they may be applied uniformly across all Object Stores. Configuration objects: Directories: All CM authentication and authorization ultimately comes down to data obtained from an LDAP directory. Some of those lookups are done by the application server, and some are done directly by the CE server. The directory configuration objects tell the CE server how to communicate with that directory or directories. Subsystem configurations: There are several logical subsystems within the CE that are controlled by their own fl avors of subsystem configuration objects. Examples include trace logging configuration and CSE configuration. These are typically configured at the domain level and inherited by lower level topology nodes. A description of topology nodes is coming up in the next section of this article. Pointers to components: Content Cache Areas: The Domain contains configuration information for content caches, which are handy for distributed deployments. Rendition Engines: The Domain contains configuration and connectivity information for separately installed Rendition Engines (sometimes called publishing engines). Fixed Content Devices: The domain contains configuration and connectivity information for external devices and federation sources for content. PE Connection Points and Isolated Regions: The domain contains configuration and connectivity information for the Process Engine. Object Stores: The heart of the CE ecosystem is the collection of ObjectStores. Text Search Engine: The Domain contains configuration and connectivity information for a separately-installed Content Search Engine. In addition to the items directly available in the tree view shown above, most of the remainder of the items contained directly within the Domain are available one way or another in the pop-up panel you get when you right-click on the Domain node in FEM and select Properties. The pop-up panel General tab contains FEM version information. The formatting may look a little strange because the CM release number, including any fix packs, and build number are mapped into the Microsoft scheme for putting version info into DLL properties. In the previous figures, 4.51.0.100 represents CM 4.5.1.0, build 100. That's reinforced by the internal designation of the build number, dap451.100, in parentheses. Luckily, you don't really have to understand this scheme. You may occasionally be asked to report the numbers to IBM support, but a faithful copying is all that is required. Topology levels There is an explicit hierarchical topology for a Domain. It shows up most frequently when configuring subsystems. For example, CE server trace logging can be configured at any of the topology levels, with the most specific configuration settings being used. What we mean by that should be clearer once we've explained how the topology levels are used. You can see these topology levels in the expanded tree view in the left-hand side of FEM in the following screenshot: At the highest level of the hierarchy is the Domain, discussed in the previous section. It corresponds to all of the components in the CE part of the CM installation. Within a domain are one or more sites. The best way to think of a site is as a portion of a Domain located in a particular geographic area. That matters because networked communications differ in character between geographically separate areas when compared to communications within an area. The difference in character is primarily due to two factors—latency and bandwidth. Latency is a characterization of the amount of time it takes a packet to travel from one end of a connection to another. It takes longer for a network packet to travel a long distance, both because of the laws of physics and because there will usually be more network switching and routing components in the path. Bandwidth is a characterization of how much information can be carried over a connection in some fixed period of time. Bandwidth is almost always more constrained over long distances due to budgetary or capacity limits. Managing network traffic traveling between geographic areas is an important planning factor for distributed deployments. A site contains one or more virtual servers. A virtual server is a collection of CE servers that act functionally as if they were a single server (from the point of view of the applications). Most often, this situation comes about through the use of clustering or farming for high availability or load balancing reasons. A site might contain multiple virtual servers for any reason that makes sense to the enterprise. Perhaps, for example, the virtual servers are used to segment different application mixes or user populations. A virtual server contains one or more servers. A server is a single, addressable CE server instance running in a J2EE application server. These are sometimes referred to as physical servers, but in the 21st century that is often not literally true. In terms of running software, the only things that tangibly exist are individual CE servers. There is no independently-running piece of software that is the Domain or GCD. There is no separate piece of software that is an Object Store (except in the sense that it's a database mediated by the RDBMS software). All CE activity happens in a CE server. There may be other servers running software in CM—Process Engine, Content Search Engine, Rendition Engine, and Application Engine. The previous paragraph is just trying to clarify that there is no piece of running software representing the topology levels other than the server. You don't have to worry about runtime requests being handed off to another level up or down the topological hierarchy. Not every installation will have the need to distinguish all of those topology levels. In our all-in-one installation, the Domain contains a single site. That site was created automatically during installation and is conventionally called Initial Site, though we could change that if we wanted to. The site contains a single virtual server, and that virtual server contains a single server. This is typical for a development or demo installation, but you should be able to see how it could be expanded with the defined topology levels to any size deployment, even to a deployment that is global in scope. You could use these different topology levels for a scheme other than the one just described; the only downside would be that nobody else would understand your deployment terms. Using topology levels We mentioned previously that many subsystems can be configured at any of the levels. Although it's most common to do domain-wide configuration, you might, for example, want to enable trace logging on a single CE server for some troubleshooting purpose. When interpreting subsystem configuration data, the CE server first looks for configuration data for the local CE server (that is, itself). If any is found, it is used. Otherwise, the CE server looks for configuration data for the containing virtual server, then the containing site, and then the Domain. Where present, the most specific configuration data is used. A set of configuration data, if used, is used as the complete configuration. That is, the configuration objects at different topology levels are not blended to create an "effective configuration". CE has a feature called request forwarding. Because the conversation between the CE server and the database holding an Object Store is chattier than the conversation between CE clients and the CE server, there can be a performance benefit to having requests handled by a CE server that is closer, in networking terms, to that database. When a CE server forwards a request internally to another CE server, it uses a URL configured on a virtual server. The site object holds the configuration options for whether CE servers can forward requests and whether they can accept forwarded requests. Sites are the containers for content cache areas, text index areas, Rendition Engine connections, storage areas, and Object Stores. That is, each of those things is associated with a specific site.
Read more
  • 0
  • 0
  • 4100

article-image-aspnet-using-jquery-ui-widgets
Packt
29 Apr 2011
5 min read
Save for later

ASP.NET: Using jQuery UI Widgets

Packt
29 Apr 2011
5 min read
ASP.NET jQuery Cookbook Over 60 practical recipes for integrating jQuery with ASP.NET      The reader can benefit from the previous article on ASP.NET: Creating Rich Content. Using the datepicker control The datepicker is a popular control for date fields in online submission forms. In this recipe, let's see how to use the jQuery UI to attach a datepicker to an ASP.NET TextBox control. Getting Ready Create a new web form Recipe5.aspx in the current project. Add controls to create a simple search form that accepts an input date field as follows: <form id="form1" runat="server"> <div align="center"> <asp:Label ID="lblDate" runat="server">Search by registration date: </asp:Label> <asp:TextBox ID="txtDate" runat="server"></asp:TextBox> <asp:Button ID="btnSubmit" Text="Search" runat="server" /> </div> </form> Thus, on page load, the web form appears as shown in the following screenshot: We will now use jQuery UI to attach a datepicker to the TextBox control. How to do it... In the document.ready() function of the jQuery script block, apply the datepicker() method to the TextBox control: $("#txtDate").datepicker(); Thus, the complete jQuery solution for the given problem is as follows: <script language="javascript" type="text/javascript"> $(document).ready(function(){ $("#txtDate").datepicker(); }); </script> How it works... Run the web form. On mouseclick on the TextBox control, the datepicker is displayed as shown in the following screenshot: The desired date can be picked from the displayed calendar as required. There's more… For detailed documentation on the jQuery UI datepicker widget, please visit http://jqueryui.com/demos/datepicker/. Using the progress bar control jQuery UI provides a Progressbar widget to show the processing status during a wait time in an application. In this recipe, we will learn to create a Progressbar in ASP.NET. Getting Ready Include an animated gif file pbar-ani.gif in the images folder in the project. Add a new web form Recipe6.aspx to the current project. Add an ASP.NET panel control for the progressbar as follows: <asp:Panel id="progressbar" runat="server"></asp:Panel> Define some basic css style for the above as follows: #progressbar { width:300px; height:22px; } The jQuery UI progressbar uses the jQuery UI CSS Framework for styling. Hence, to set the background of the progressbar to the animated gif file, add the following css style: .ui-progressbar-value { background-image: url(images/pbar- ani.gif); } Create another content panel that is initially hidden and displayed only after the progressbar loads completely. <asp:Panel id="contentArea" runat="server">Page successfully loaded</asp:Panel> We will use the following css class to hide this panel: .hide 21 { display:none; } Thus, the complete aspx markup of the form is as follows: <form id="form1" runat="server"> <div align="center"> <asp:Panel id="progressbar" runat="server"></asp:Panel> <asp:Panel id="contentArea" runat="server">Page successfully loaded</asp:Panel> </div> </form> Now, we will look at the jQuery solution for applying the Progressbar widget to the ASP.NET panel. How to do it... In the document.ready() function of the jQuery script block, hide the display message: $("#contentArea").addClass("hide"); Initialise a counter: var cnt = 0; Define the maximum value of the counter: var maxCnt = 100; Use the JavaScript timer function setInterval() to define the timeout interval and the callback function after each interval: var id = setInterval(showprogress, 10); Now, define the previous callback function: function showprogress() { Check if the current value of the counter is less than or equal to the maximum allowable value: if (cnt <= maxCnt) { If yes, then apply the progressbar() function with the current counter value: $("#progressbar").progressbar({ value: cnt }); Increment the counter: cnt++; } If the current value of the counter is greater than the maximum value, clear the timer using the respective ID: lse { clearInterval(id); Show the display message: $("#contentArea").removeClass("hide"); Hide the progress bar: $("#progressbar").addClass("hide"); } } Thus, the complete jQuery solution is a follows: <script language="javascript" type="text/javascript"> $(document).ready(function() { $("#contentArea").addClass("hide"); var cnt = 0; var maxCnt = 100; var id = setInterval(showprogress, 10); function showprogress() { if (cnt <= maxCnt) { $("#progressbar").progressbar({ value: cnt }); cnt++; } else { clearInterval(id); $("#contentArea").removeClass("hide"); $("#progressbar").addClass("hide"); } } }); </script> In this solution, we have used the JavaScript timer setInterval(customFunction, timeout) to call a custom function after the timeout (in milliseconds). Important points to note are: The setInterval method returns a numeric number,id to track the timeout. This ID can be later used to clear the timer. The timer calls the custom function showprogress() repeatedly after every timeout interval until the clearInterval(id) is called. After every timeout, we will increment the variable cnt by 1 and apply it to the progressbar. When cnt reaches maxCnt, the progressbar loads completely. How it works... Run the web form. You will see that the progressbar loads in steps, as shown in the following screenshot: After the load is complete, the progressbar is hidden and the content panel is displayed instead, as follows: There's more… For detailed documentation on the jQuery UI progressbar widget, please visit http://jqueryui.com/demos/progressbar/.  
Read more
  • 0
  • 0
  • 4098
Unlock access to the largest independent learning library in Tech for FREE!
Get unlimited access to 7500+ expert-authored eBooks and video courses covering every tech area you can think of.
Renews at ₹800/month. Cancel anytime
article-image-social-bookmarking-blogger-part-1
Packt
12 Oct 2009
7 min read
Save for later

Social Bookmarking in Blogger: Part 1

Packt
12 Oct 2009
7 min read
The features of social bookmarking sites are in constant evolution. Currently they can be broadly categorized into three types: User generated news: The main goal is to increase visits by getting on the front page of a site like Digg or Reddit. This will increase traffic to a site by huge amounts for anywhere from a few minutes to a day. Sites unprepared for the avalanche of hits often choke on the visitor overload. This is commonly known as the Slashdot effect (http://www.slashdot.org); a popular technologies site whose readers have broken many a site under the crush of their visits. Circle of friends sharing: When posting to Facebook (http://www.facebook.com), Twitter (http://www.twitter.com), Flickr (http://flickr.com), or a blog, the user knows that the main purpose of these sites is sharing content with friends and people. When a user shares a link with a friend, a slight increase in traffic may occur (unless the user is a "celebrity" blogger with thousands of followers). Focusing on such groups would be more effective for smaller blogs. Online bookmarks: Readers use these sites to manage their bookmarks online. Links can be public, and may even serve the public interest, such as "How To". Most people see these sites as a welcome alternative to trying to export or duplicate bookmarks across multiple browsers or computers. Adding links to these sites will increase the chance of first time readers becoming regulars. Examples include del.icio.us (http://del.icio.us), Furl (http://www.furl.net), and Ma.gnolia (http://ma.gnolia.com). How Social Bookmarking Works Social bookmarking works because people share information they find online with each other. The different features that social bookmark services such as online bookmarks, categories, and rss feeds provide make it easier for people to find sites that interest them in new and sometimes unpredictable ways. People are connected to each other through these services, forming social and interacting networks, helping others find information, and spreading the word about sites they enjoy. Submitting Posts without Bookmarks Bookmarks are convenient for readers and bloggers. Submitting articles and posts manually is extra time and work for a reader. Making it easier for them by linking the post title and URL automatically encourages readers to submit posts spontaneously. Let's recommend a site to Reddit (http://www.reddit.com) without using bookmarks. Reddit is a popular online bookmark and user-generated news service. Time for Action!—Become a "Bookmarker" Navigate to http://www.reddit.com and click the submit link at the top of the screen. You will be redirected to the register or login screen. A username and password are all that is needed. Enter a username into the username box. You can enter an email address such as fruitforall@gmail.com into the email text field. Type a password into the password box and again into the verify password box. You can choose to have the site remember your login for you by clicking on the remember me checkbox. After reviewing the privacy policy and user agreement pages, place a check in the box next to I understand…. Click on the create account button after the form has been filled out as shown in the following screenshot: Find an interesting article to submit. We will submit the latest post on the (http://cookingwithamy.blogspot.com) blog. An example of the post being submitted is shown in the following screenshot. Copy the URL and the title of the post into a text editor such as Notepad (Windows) or Textpad (Mac). Log in to Reddit and click the submit link. Enter all the data manually, as shown in the following screenshot. Click the submit button. The link has now been shared. What Just Happened? It took three steps to add one link to Reddit. That did not include the time spent finding the site we wanted to submit. Then we had to log in to the bookmarking service and go to the submit form. We had to copy all the submission information ourselves and then enter it all manually into the bookmarking site form. The URL had to be entered correctly. If we had made a mistake while typing, the process would have taken longer and been more frustrating. It took a minute or two instead of the few seconds a bookmark would take. Now let's see how social bookmarks are a useful addition to our blog. They save the readers' time and make it more likely that new readers will impulse bookmark. Sharing Posts by Email A common way for visitors to share posts and articles they like is to email them to other people. Blogger has an Email Post to a Friend feature. Using features that make sharing posts more convenient for visitors will increase the exposure of your blog. This is a small subset of a type of marketing known as viral marketing, where readers spread your message for you from one person to another. "How hard is it to turn on this feature?" asks Georgia as she navigates the blog. "I'd like to try it. Then my readers will have an easy way to share my posts!" Time for Action!—Turn On Email Posting Log in to the blog, click the Settings link, and navigate to the Basic sub tab link. Scroll down the list to Show Email Post links? and select Yes from the drop‑down list as shown in the following screenshot: Click on Save Settings. Now it's time to test the feature. View the blog and click on the small email icon below the post. The Email Post to a Friend screen will appear. The sender will need to enter his name and email address and the email address of the person he wants to send the post to. The Message box, which is not a required field, can contain any notes from the sender. A sample of the post content is displayed at the bottom of the screen. Click the Send Email button to send the message. An email will be sent to the address fruitforall@gmail.com, and a success screen is displayed with a link back to the blog, as shown in the following screenshot: The submitter will be able to return to the blog using the link under Return to where you were on the confirmation screen. What Just Happened? When you logged into the blog and turned on the email post links feature, The Email post link setting in the blogger template was set to "show". The icon for the Email-Post-To-A-Friend feature was then visible under each blog post. Clicking on the icon brought up a new screen with a form that prompted the submitters to enter the email information for themselves and their friends. The code displayed the post at the bottom of the screen, automatically. The friend is then sent an email with a link to the post. Adding Bookmarks to Blogs Social bookmarks can be displayed on blogs as text links, buttons, or as dynamic mini‑widgets showing the number of submissions. Adding bookmarks to blogs is a task that ranges from simple cut and paste to custom coding. We will first choose the social bookmarks and then explore several different techniques to add them to our blog. Choosing the Right Bookmarks for Your Blog Blogs that focus on specific topics or points of views stand out from thousands of other blogs and attract a more regular following. The social bookmarks you choose should fit the subject and tone of your blog. A technology blog would most likely have bookmarks to Digg (http://www.digg.com), Slashdot (http://www.slashdot.org), and Reddit (http://www.reddit.com). "There are so many social bookmarking services out there," says Georgia. "How do I pick the ones that are right for my blog?" Earlier, we had defined three broad types of social bookmark systems. You could just choose whatever bookmark sites you see your friends using. But you're smarter than that. You are on a mission to make sure your blog post links will show up where readers interested in your topic congregate. Listed below are the most popular and useful social bookmark systems and networks.
Read more
  • 0
  • 0
  • 4074

article-image-microsoft-sql-server-2008-installation-made-easy
Packt
23 Oct 2009
3 min read
Save for later

Microsoft SQL Server 2008 - Installation Made Easy

Packt
23 Oct 2009
3 min read
(For more resources on Microsoft, see here.) Initial State of Computer Assuming you are working with the Windows XP OS, it will be advisable to create a restore point to which you can fall back should you fail to install and something goes wrong. You can set up a fall back position by going to Start | All Programs | Accessories | System Tools | System Restore. This allows you to comeback where you were before starting the install. The other thing that you should lookup is the suite of Microsoft software you already have on your computer that may interfere with the product you are installing. This can be reviewed following Start | Control Panel | Add and Remove Programs. SQL 2008 server requires IE 6.0 or higher version. It may be helpful to install this before embarking on installing the SQL 2008 Server. For the purpose of this article IE 7.0 was installed. It has appeared in some forum topics that SQL 2008 can exist side-by-side with SQL 2005 server. However in the present case SQL 2005 was completely removed. Sometimes even this removal is not quite an easy process if something is broken in the original install and requires you to reinstall and then uninstall. In the case of SQL Server 2008, there was an earlier version, "Katmai", installed but never used due to its inability to connect to the SQL Server Management Studio (Well, unless you remove the SQL 2005 client you cannot install SQL 2008 Client), a fact which came to light much later. 'Katmai' components were completely removed which required reinstalling the 'Katmai' followed by its complete removal. When you download the SQL 2008 and run the executable, it creates the folder, servers, containing a number of subfolders and files (dynamic link library files etc) that are used during the installation. Help can be accessed from servershelp1033s10ch_setup, an HTML file which provides a wealth of information regarding all aspects of installation including migration from an earlier version. From servesdefault.htm you can begin the installation which provides the required support using Prepare | Install | Other information navigational aid. After removing all the suggested components during this installation, the remaining Microsoft SQL Server related components on the computer are as shown in the Add and Remove Programs window in the next figure. The very first screen you will see when you click on the serverssetup.exe file is the SQL Server 2008 Setup where you need to agree with the licensing terms before proceeding. When you click on the Next button which displays the Installation Pre-requisites screen, you will be shown the pending items needed before you install SQL 2008 server. Click on the Install button after highlighting the pending item regarding setup support files in the right screen. SQL Server Installation Center This will take you to the SQL Server Installation Center screen as shown. It has a number of useful hyperlinks that you can come back to by repeating the above steps. Click on New Installation link. This Starts Install SQL Server 2008 Wizard for System Configuration Check. After a while when the checking is completed the following screen will be displayed. This timeall items have the status marked 'Passed'. In a previous attempt when the 'Katmai' items were still uninstalled,the Previous CTP Install Check did not succeed and it was corrected only after completely removing those items. Clicking on Next button takes you to screen where you need to select the features that you want to have installed as shown. The display shows Features Selection window after all items have been checked.
Read more
  • 0
  • 0
  • 4074

article-image-how-get-incoming-links-joomla-15-seo-part-2
Packt
19 Nov 2009
9 min read
Save for later

How to get Incoming Links in Joomla! 1.5 SEO: Part 2

Packt
19 Nov 2009
9 min read
WordPress As I mentioned before, WordPress is the biggest scoring free service that you can use. It is also the only one that doesn't allow you to spam their system and use it just for promotional actions. All the other services mentioned earlier allow you to monetize your blog or web site. Some share a portion of their revenue as well. So, if you want to make some money on the side, these services will provide you with the possibility to do so. WordPress doesn't allow you to build blogs just for Search Engine Optimization and I quote: We have a very low tolerance for blogs created purely for Search Engine Optimization or commercial purposes, machine-generated blogs, and will continue to nuke them. So if that's what you're interested in, WordPress is not for you. A self-hosted solution would be much more appropriate for you; suitable hosts can be found at http://www.wordpress.org/ hosting. Also see the following text taken from http://support.wordpress.com/advertising: This might be just one of the reasons that Google loves WORDPRESS.COM blogs. So how is it possible to use WORDPRESS.COM to promote your website? Actually, you don't. On this service you are not going the promote your site in a way that you can do on the other services. On WORDPRESS.COM you truly build a blog or site containing pages with true value to the visitors of that blog. You can create an About page where you put a link to your main website and in that way show the readers where to get more information. You can also put a link to your website in the link section (Blogroll) together with a few other relevant links that contain valid information. Blogging on WordPress and your ranking If you cannot promote your web site in a big way then what is the point of creating a blog on WORDPRESS.COM? A blog on WordPress can rank highly for the topic that you are blogging about and will give you some SEO love through those rankings. What is more important is the fact that you can take a special topic from your main web site's topic and create a blog around that. If you write your blog posts well and start to rank on that topic you will be seen as an authority on that topic and people will want to know more about you. That is the main reason to invest time to blog on WORDPRESS.COM to be recognized as an authority in your field of expertise. As you took only one topic out of all the topics that your site is about, you can do it again for another topic as well. You could also see these blogs as a collection of topic silos that create an array of highly related web sites that point to yours. This kind of link building takes time, and a lot of it! Is it worth it? Yes most certainly, and in more ways than one. With blogging you can achieve the following: An authority status if you do it right More traffic to your web site Better rankings in the search engines More insight into what the visitors of your web site are looking for To interact with other people having interest in the same topic as you Fun in writing and that will reflect on your site as you want to create more content on that site as well There is also a downside that you have to consider—it takes time away from building content on your main site and you have to cover more locations to maintain in the beginning. If you use that blog to write some timeless quality content on a niche part of your main site you will find out that you can stop maintaining those blogs after a short period of time. Remember, these are valid blogs to build incoming links to your main site! Digging deeper into WORDPRESS.COM blogs Creating a blog on WordPress is also very simple, go to WORDPRESS.COM and get a blog. Wait! Don't go yet! You need a few guidelines to start. Your initial user account name is going to be the first part of your URL, so name it right and remember, you cannot use a "-" in your username. My first account was seo4joomla so what I got was seo4joomla.wordpress.com. When you are logged in to WORDPRESS.COM and you type in the URL with a new keyword that you want (if it is not taken); you will get the option to add that blog to your account so that you can manage all of your WordPress.COM blogs from one place. Think about the title of your blog, if you want to change it later you can do that in the settings panel. Once you have your new blog, start cleaning. Delete the sample post and the comment along with it. Delete all the links in the blogroll (unless you are going to write about WordPress). Change the base post category from Uncategorized to a relevant topic name. Change the name of the links category from Blogroll to your most relevant keyword. Delete the About page and create a new one with the keywords of your blog in the title. That way your URL (page slug in WordPress) is containing the same keywords. Choose a nice theme layout that fits your topic, and if possible use a customized header. Using a customized header will give your site a slightly different look from the other WORDPRESS.COM web sites. Change the tagline in the general settings and start writing the way you do on your web site!   Using free blogging services As you saw, there are several blogging platforms and free web site building platforms that you can use to promote your web site. There are a lot more out there on the Internet, but you need to look for the ones that rank well in the search engines before you put your valued time into building a linking "empire". These services are free of charge and sometimes live on the revenue that comes from the blog content they host. If you don't want to be on such a platform where there are advertisements around your writing, don't use them. If you are afraid that you can lose your blog on such sites look for a way to make backups (for example, on WORDPRESS.COM you can use the Export function). How to minimize your blog writing time Keeping content fresh and up-to-date on all the blogs that you build is not that difficult. If you focus on blogging on your own web site, you should try to integrate the RSS Feed from your web site into those blog pages. RSS Feeds are the best possible automatic way of updating one-to-many, so use it to your advantage. Using your best content for link building Use the best articles from your web site to get into the picture of social bookmarking web sites. Find the most visited pages and the pages with the greatest number of comments, if you have a blog on your Joomla! site. Go to bookmarking sites and bookmark your pages using your own account. There are a lot of bookmarking web sites that you can use, just make sure you send your bookmarks to at least the following: Delicous Digg Reddit Newsvine Bloglines StumbleUpon These are some of the most influential ones that count towards your search engine ranking and are a great way to get traffic. Traffic from this kind of web site will come in bursts and mostly will not span a longer time period than a few days. The real power lies in the long term effect. Writing articles for links If you like writing about your passion, you can consider writing articles and submitting them to article publishing services. People are always looking for information and, if you can provide that to them in a smart way, it will help you to gain recognition as a field expert. You don't have to write long articles, but they must be informative and should give the reader an answer to a question they might have. Write those articles and submit them to services such as: www.thewhir.com www.ideamarketers.com www.goarticles.com www.ezinearticles.com Each of those services have their own "Terms of Service" that you should read before submitting your articles. They have their quality guidelines as well. The length of the article might need to be of a certain minimum or maximum number of characters. You might not be permitted to link deeper into your web site than the top level. Get that information before you choose a service to work with. Depending on the number of webmasters that will use your articles to republish, you could get a lot more incoming links from just a few well-written articles. What you should NOT do is take an old article from your site and send it as an article to be republished. That could backfire, as the services mentioned have a clause in their "Terms of Service" stating that the article is original and not published before. You should really not republish an already submitted article on your own web site, it could give your site a duplicate content penalty as that article will be published all over the Internet (with your link in it). An alternative could be that you publish some of your articles combined and rewritten into an e-book in PDF format that you give away for free from your web site.  
Read more
  • 0
  • 0
  • 4071

article-image-drupal-7-module-development-drupals-theme-layer
Packt
14 Dec 2010
16 min read
Save for later

Drupal 7 Module Development: Drupal's Theme Layer

Packt
14 Dec 2010
16 min read
  Drupal 7 Module Development Create your own Drupal 7 modules from scratch Specifically written for Drupal 7 development Write your own Drupal modules, themes, and libraries Discover the powerful new tools introduced in Drupal 7 Learn the programming secrets of six experienced Drupal developers Get practical with this book's project-based format Business logic versus presentation logic So what would be the best way to get our data and functionality marked up? Do we simply wrap each piece of data in HTML and return the whole as a giant string? Like the following example: return '<div class="wrapper">' . $data . '</div>'; Fortunately, we don't. Like all other well-designed applications, Drupal separates its business logic from its presentation logic. Traditionally, the primary motivations for this separation of concerns are as follows: To make the code easier to maintain. To make it possible to easily swap out one layer's implementation without having to re-write the other layers. As we shall see, Drupal takes the "swap-ability" aspect to the extreme. As we mentioned in the introduction of this article, the default theme selected on the Appearance page is the most obvious part of the theme layer. Also, you might think that the theme is responsible for applying the HTML and CSS for the website. However, there are thousands of contributed modules on drupal.org. Should the theme be responsible for marking up all of those modules' data? Obviously not. Since a module is most intimately familiar with its own data and functionality, it's the module's responsibility to provide the default theme implementation. As long as the module uses the theme system properly, a theme will be able to override any HTML and CSS by hot-swapping its own implementation for the module's implementation. After the data has been retrieved and manipulated in the heart of your module (the business logic), it will need to provide the default theme implementation. Sometimes a particular theme will need to override your implementation in order for it to achieve a specifc design goal; if the theme provides its own implementation, Drupal will use the theme implementation instead of the module's default implementation. $variables = array('items' => $list, 'type' => 'ol'); $content = theme('item_list', $variables); By calling the theme() function, we are delegating the responsibility of determining and using the proper theme implementation. We're saying: "Hey, theme()! I want to markup my data as an item_list. Can you do that for me? I don't need to know the details. kthxbye." Our module just needs to decide which theme hook it wants to use to markup its data. Should the data be displayed in an unordered list, a table, or a wordle? Hook crazy? In addition to API hooks, Drupal also has theme hooks. A theme hook is simply the name of a particular way to markup some data. For example, passing data to the item_list theme hook will result in different markup then passing data to the links theme hook. However, while normally every module's hook function will be called when Drupal invokes an API hook, only one theme hook implementation will be invoked when Drupal invokes a theme hook. There are actually two different ways you can make an implementation (which we will discuss later), but for now we'll only talk about the simplest method for module developers—theme functions. When you call theme(), it will look for a default theme function named theme_HOOKNAME and for an optional theme override function called THEMENAME_HOOKNAME. If you dig into Drupal's internals, you'll fnd a theme_item_list() inside includes.inc or theme.inc. This is Drupal's default theme implementation for an item_list. If our active theme was Bartik, and if Bartik implemented a theme override called bartik_item_list(), then theme() would use the Bartik theme's implementation instead of the default one. The preceding fgure shows one piece of data as it passes through a module and a theme. However, in order for you to understand the full power of Drupal's theme layer, you also need to understand how the entire page is built. However, since all of the active theme's modifcations occur after any module modifcations, from a module developer's perspective, all of this theme inheritance is transparent. Since modules don't need to know anything about the structure o the theme and its ancestry, we'll simply talk about "the theme" in this book. Just be aware that the actual theme may be more complex. Base themes and sub-themes If you've previously read anything about Drupal theming, you've probably heard about base themes and sub-themes. Any theme can declare a parent theme in its .info file using the base theme key and it will inherit all the hook implementations from its parent theme (and its parent's parent theme, and so on). Data granularity One of the things that makes Drupal theming so powerful is its granularity. Each piece of content is handled separately as it's passed through the theming system. Each bit of data is themed individually, then combined into ever-larger chunks. At each step in the aggregation process, it's themed again. The following illustration will make this clearer: As you can see in the preceding illustration, for a typical blog post, each comment is pulled from the database and sent through the theme system to get HTML markup added to it. Then all the comments are aggregated together into a "comment wrapper" where additional markup and, usually, a "new comment" form is added. Then the single group of comments is passed to the node theming where it is combined with other pieces of the blog post's content. This process of theming bits of content, aggregation, and theming again is repeated until we've built the entire HTML page ready to be sent to a web browser. There are two advantages to this granular system. First, since each module is responsible for theming its own data, it can either create a very specialized theme hook for its data or it can re-use an existing theme hook. Re-using a theme hook ensures a consistent set of markup for similar data structures while still allowing customized CSS classes (Most theme hooks allow custom classes to be passed as parameters.) For example, the list of links after a node (read more, add new comment, and so on) re-uses the links theme hook, and the links after each comment use the same links theme hook. The second advantage is for the theme developer. Having a fine-grained theming system means that a theme, if it chooses to, can literally rewrite all of the markup for its own design purposes. As module developers we need to be keenly aware of the themer's desire to have granular theming overrides.   Theme engines Some themes require alternate theme engines. Theme engines can provide alternate template syntax, naming standards, and helper functions. Several theme engines are available for download at http://drupal.org/project/theme+engines. However, we won't be discussing any theme engines except for Drupal's default theme engine, PHPTemplate. The PHPTemplate theme engine has been the default theme since Drupal 4.7, has been continuously improved with each version, and has proven its worth again and again. Over 99% of themes available for download on drupal.org use the default PHPTemplate theme engine. Two ways to theme So now that we have a good understanding of higher level concepts, let's get down to the nitty-gritty of theme implementations. There are actually two different ways to implement a theme hook: Theme functions: pass data to a PHP function to wrap it in markup Templates: pass data to a template which is a PHP file mixed with markup and PHP print statements Let's look at each of these in turn. Theme functions For a module developer, the easiest type of implementation to understand is a theme function. Theme functions just need to follow a few simple rules in order for them to work properly. First, the name of the theme function follows the pattern: theme_[theme hook name] Since the theme hook name is used directly in the theme function's name, theme hook names have the same constraints on naming as regular PHP function names; the only valid characters in theme hook names are alphanumeric characters and underscores. So if a module has created an example_format theme hook, it would implement it with theme function named theme_example_format(). Second, the theme function will only have a single parameter, as follows: function theme_THEME_HOOK_NAME($variables) {…} The theme function variables are an associative array containing the pieces of data we wish to markup and any options we want to pass to the function. It may seem extremely odd not to use multiple parameters and PHP's ability to specify default values for each parameter. In fact, previous versions of Drupal did use multiple parameters. We'll see why Drupal now only uses one parameter in just a moment when we talk about preprocess functions. For an example of a $variables array, let's look at how the DocBlock of the theme_item_list() function defnes it: Items: An array of items to be displayed in the list. If an item is a string, then it is used as is. If an item is an array, then the data element of the array is used as the contents of the list item. If an item is an array with a "children" element, those children are displayed in a nested list. All other elements are treated as attributes of the list item element. Title: The title of the list. Type: The type of list to return (e.g. ul,ol). Attributes: The attributes applied to the list element. The items and title keys hold the actual data, and the type and attributes keys are options that specify how to build the item list. Third, the theme function should return a string that contains the rendered representation of the data. This is usually a string of HTML, but some theme hooks return other types of themed markup. For example, theme_syslog_format returns a simple string with pipe-separated data values for use in a *NIX syslog error log. That's it! As you can see, theme functions have very simple requirements and in every other way are standard PHP functions. The major difference between most functions and theme functions is that you should never call theme functions directly. It may be tempting to take your data and call theme_item_list($vars) directly, but you should instead call theme("item_list", $vars). This method of calling theme functions indirectly ensures that themes are able to override any module's default theme function (or template). It also allows the theme() function to work additional magic, including allowing other modules to alter the theme function's variables before they are used. Preprocess functions Now we're starting to see the real flexibility of the theme system. Preprocess functions allow one module to alter the variables used by another module when it calls a theme hook. So if some code passes data to theme() for a particular theme hook, preprocess functions will be called to alter the data before the actual theme hook implementation is called. The following steps are carried out: Code calls theme('hook_name', $variables). theme() calls preprocess functions for hook_name. Preprocess functions modify variables. theme() calls actual implementation for hook_name with modifed variables. All preprocess functions take the form of: [module]_preprocess_[theme hook name](&$variables) So if the foo module wants to alter the variables for the item_list theme hook, it could define the function as follows: function foo_preprocess_item_list(&$variables) { // Add a class to the list wrapper. $variables['attributes']['class'][] = 'foo-list'; } Notice that the $variables parameter is defned with an ampersand in front of it. That's PHP notation to pass the parameter by reference. Instead of getting a copy of the variables, the foo_preprocess_item_list() function will get access to the actual $variables which is later passed to the theme function implementation. So any modifications that the preprocess function makes to the $variables parameter will be preserved when those variables are passed to the theme function. That's the reason our example foo_preprocess_item_list() function doesn't return anything; its work is done directly on the original $variables. This is extremely handy for module developers as it allows all sorts of integration with other modules. Since the variables parameter is a mix of data and options, modules can alter both the raw data and change the way data will be rendered. This can be as simple as one module needing a special class for use in its JavaScript code and adding that class to another module's themed content by appending to the $variables['attributes']['class'] array, or can be more complex interactions like the i18n module translating the language used in blocks. Imagine we've built a retro module that integrates GeoCities and we want to replace all links to a user's profle page with a link to the user's GeoCities homepage. We can do that relatively easily with a preprocess function. First let's look at the following theme_username function's documentation: /** * Format a username. * * @param $variables * An associative array containing: * - account: The user object to format. * - name: The user's name, sanitized. * - extra: Additional text to append to the user's name, sanitized. * - link_path: The path or URL of the user's profile page, home * page, or other desired page to link to for more information * about the user. * - link_options: An array of options to pass to the l() function's * $options parameter if linking the user's name to the user's * page. * - attributes_array: An array of attributes to pass to the * drupal_attributes() function if not linking to the user's page. */ Quite conveniently, theme_username() has a handy $link_path variable that we want to alter to achieve our old-school giggles. Assuming that we've used some other business logic with the user module's hooks to load our GeoCities URL into the user's account (the "hard" part), replacing the link to the user's profle page can be accomplished with the following simple preprocess function: /** * Implements awesomeness with hook_preprocess_username(). */ function retro_preprocess_username(&$variables) { $variables['link_path'] = $variables['account']->geocities_url; } That's it! We don't have to override the user module's theme implementation; we just modify its parameters. Theme overrides While module developers usually don't have to worry about whether a theme overrides a particular theme function or not, it's still important to understand how this mechanism works. Drupal theme is normally composed of CSS, images, JavaScripts, template files (discussed shortly), a .info file, and a template.php file. The template.php file is analogous to a module's .module file. It contains all of the PHP functions for the theme and is automatically loaded when the theme is initialized. If a theme wants to override a particular theme function, it needs to copy the theme function from its original location and paste it into its template.php file. Then it needs to change the function's prefix from theme to its own name and finally, it needs to start making the desired changes to the function. For example, if the Bartik theme wants to override the theme_menu_local_tasks() function in order to add some markup around the page's tabs, it would copy the entire function from includes/menu.inc, paste it into Bartik's template.php, and rename it to bartik_menu_local_tasks(). Fortunately, when a theme overrides a default theme function, a module's preprocess functions continue to work as normal. Themes also have the ability to create preprocess functions. If the Bartik theme decides to format a user's name in "last name, frst name" format, it can implement a bartik_preprocess_username() function. Fortunately, a theme's preprocess functions do not override a module's preprocess functions. All preprocess functions are run; frst any module's preprocess functions and then the theme's preprocess function. Template files While theme functions might be the easiest for module developers to understand, template files are the easiest for themes to grasp. When a theme hook is implemented with template files, they are used instead of theme functions. However, from a module developer's standpoint, there is actually a remarkable amount of similarity between template files and theme functions. First, let's take a closer look at template files. Templates are files primarily containing HTML but with some PHP statements mixed in using the template's variables. Instead of declaring a theme_hook_name() function, a module would instead create a hook-name.tpl.php file. The following are the contents of a typical template file, typical-hook.tpl.php: <div class="<?php print $classes; ?>"<?php print $attributes; ?>> <?php if ($title): ?> <h2<?php print $title_attributes; ?>> <?php print $title; ?> </h2> <?php endif;?> <div class="submitted"> <?php print t('By !author @time ago', array( '@time' => $time, '!author' => $author, )); ?> </div> <div class="content"<?php print $content_attributes; ?>> <?php // We hide the links now so that we can render them later. hide($content['links']); print render($content); ?> </div> <?php print render($content['links']); ?> </div> The preceding example shows the full gamut of the things that you are likely see in a template file. They are as follows: Printing a variable containing a string? Printing a translatable string using t() Conditional if/else/endif statement Delaying rendering on part of a render element with hide() Printing a render element All of the PHP in a template should be limited to printing out variables. This limited amount of PHP makes it much easier for non-programmers to learn how to use template fles compared to theme functions. However, for module developers, the template implementation is still very similar to the theme function implementation; the handful of differences are relatively minor. As with theme function implementations, our module would still need to invoke the theme hook using theme(). $variables = array('typical' => $typical_object); $output = theme('typical_hook', $variables); The theme() function would discover that the typical_hook theme hook was implemented as a template and render the corresponding typical-hook.tpl.php file. The only valid characters in theme hook names are alphanumeric characters and underscores. This is true of all theme hooks, regardless of whether they are implemented as a theme function or as a template file. However, when theme() looks for template implementations, it will automatically convert any underscores in the theme hook name into hyphens while searching for the template file. For example, calling theme('user_picture', $variables) will result in the template file named user-picture.tpl.php being rendered. Also, just like theme functions, other modules can modify the variables using preprocess functions. In template fles the focus is on printing out variables in various places in the markup. So for template fles, the preprocess function takes on a more important role. The only difference between a theme function's preprocess functions and a template file's are the number and type of preprocess functions.
Read more
  • 0
  • 0
  • 4059
article-image-drupal-and-ubercart-2x-new-approach-drupal-theming
Packt
31 Mar 2010
3 min read
Save for later

Drupal and Ubercart 2.x: A new Approach to Drupal Theming

Packt
31 Mar 2010
3 min read
Fusion Theming System with Skinr module At the end of this article, we're going to give you a brief reference to the Fusion Theming System. It was introduced only a few months ago and it's still under heavy development. It's a base theme, meaning that you can create your own subthemes easily, using the Fusion Starter, a commented starter theme created especially for this reason. It uses a 960px or fluid 16-column grid, and its main advantage is that, with the help of Skinr module, it creates l ayout and style confi guration options that the site administrator can control using the website's User Interface, without messing with CSS. So, let's see how to install it, and how to use it for simple customizations. First navigate to http://drupal.org/project/skinr, and right after you download the module, upload and unzip to your site folder (/sites/all/modules). Then, activate the module from Administration | Site building | Modules. Navigate to http://drupal.org/project/fusion, and right after you download the theme, upload it and unzip it to your site folder (/sites/all/themes). Then, go to Administration | Site building | Themes, enable both Fusion Core and Fusion Starter themes and set the Fusion Starter theme as the default one. Browse to admin | build | themes | settings | fusion_starter to configure the settings of Fusion Starter theme. There you will find the default settings of every Drupal theme, such as logo image settings or shortcut icon settings. However, there is also a new section, named Fusion theme settings. There, you can easily change the basic styles and the layout of your theme, such as font family, font size, fixed or fluid layout without using any CSS at all. Click on Save configuration to store your settings. Now, if you hover the cursor over any block of your site, you will see a new icon. Clicking on it allows you to configure the properties of this block. You can change the width of the block, the block position, the content alignment, and apply custom styles to the elements of the block, such as padding, border, equal heights, or multi-column menus. There are also special settings for every content type. For example, if you go to Administer | Content Management | Content Types | Product, you will see two new sections, named Skinr node settings and Skinr comment settings, where you can apply custom styles to the product page and the product comments. If you want to create your own custom styles for your theme, and present them in the User Interface, you have to study the documentation of the Skinr module, available at http://www.drupal.org/node/578574.
Read more
  • 0
  • 0
  • 4042

article-image-preparing-and-configuring-your-magento-website
Packt
10 Jan 2014
8 min read
Save for later

Preparing and Configuring Your Magento Website

Packt
10 Jan 2014
8 min read
(For more resources related to this topic, see here.) Focusing on your keywords We'll focus on three major considerations when choosing where to place our keywords within a Magento store: Purpose : What is the purpose of optimizing this keyword? Relevance : Is the keyword relevant to the page we have chosen to optimize it for? Structure : Does the structure of the website re-enforce the nature of our keyword? The purpose for choosing keywords to optimize on our Magento store must always be to increase our sales. It is true that (generically speaking) optimizing keywords means driving visitors to our website, but in the case of an e-commerce website, the end goal—the true justification of any SEO campaign—must be increasing the number of sales. We must then make sure that our visitors not just visit our website, but visit with the intention of buying something. The keywords we have chosen to optimize must be relevant to the page we are optimizing them on. The page, therefore, must contain elements specifically related to our keyword, and any unrelated material must be kept to a minimum. Driving potential customers to a page where their search term is unrelated to the content not only frustrates the visitor, but also lessens their desire to purchase from our website. The structure of our website must complement our chosen keyword. Competitive phrases, usually broader phrases with the highest search volume, are naturally the hardest to optimize. These types of keywords require a strong page to effectively optimize them. In most cases, the strength of a page is related to its level or tier within the URL. For example, the home page is normally seen as being the strongest page suitable for high search volume broad phrases followed by a tiered structure of categories, subcategories, and finally, product pages, as this diagram illustrates: With that said, we must be mindful of all three considerations when matching our keywords to our pages. As the following diagram shows, the relationship between these three elements is vital for ensuring not only that our keyword resides on a page with enough strength to enable it to perform, but also that it has enough relevance to retain our user intent at the same time as adhering to our overall purpose: The role of the home page You may be forgiven for thinking that optimizing our most competitive keyword on the home page would lead to the best results. However, when we take into account the relevance of our home page, does it really match our keyword? The answer is usually that it doesn't. In most cases, the home page should be used exclusively as a platform for building our brand identity . Our brand identity is the face of our business and is how customers will remember us long after they've purchased our goods and exited our website. In rare cases, we could optimize keywords on our home page that directly match our brand; for example, if our company name is "Wooden Furniture Co.", it might be acceptable to optimize for "Wooden Furniture" on our home page. It would also be acceptable if we were selling a single item on a single-page e-commerce website. In a typical Magento store, we would hope to see the following keyword distribution pattern: The buying intention of our visitors will almost certainly differ between each of these types of pages. Typically, a user entering our website via a broad phrase will have less of an intention to buy our products than a visitor entering our website through a more specific, product-related search term. Structuring our categories for better optimization Normally, our most competitive keywords will be classified as broad keywords, meaning that their relevance could be attributed to a variety of similar terms. This is why it makes sense to use top-level or parent categories as a basis for our broad phrases. To use our example, Wooden Furniture would be an ideal top-level category to contain subcategories such as 'Wooden Tables', 'Wooden Chairs', and 'Wooden Wardrobes', with content on our top-level category page to highlight these subcategories. On the Magento administration panel, go to Catalog | Manage Categories . Here, we can arrange our category structure to match our keyword relevance and broadness. In an ideal world, we would plan out our category structure before implementing it; sadly, that is not always the case. If we need to change our category structure to better match our SEO strategy, Magento provides a simple way to alter our category hierarchy. For example, say we currently have a top-level category called Furniture , and within this category, we have Wooden Furniture , and we decide that we're only optimizing for Wooden Furniture ; we can use Magento's drag-and-drop functionality to move Wooden Furniture to become a top-level category. To do this, we would have to perform the following steps: Navigate to Catalog | Manage Categories . Drag our Wooden Furniture category to the same level as Furniture . We will see that our URL has now changed from http://www.mydomain.com/furniture/wooden-furniture.html to http://www.mydomain.com/wooden-furniture.html. We will also notice that our old URL now redirects to our new URL; this is due to Magento's inbuilt URL Rewrite System. When moving our categories within the hierarchy, Magento will remember the old URL path that was specified and automatically create a redirect to the new location. This is fantastic for our SEO strategy as 301 redirects are vital for passing on authority from the old page to the new. If we wanted to have a look at these rewrites ourselves, we could perform the following steps: Navigate to Catalog | URL Rewrite Management . From the table, we could find our old request path and see the new target path that has been assigned. Not only does Magento keep track of our last URL, but any previous URLs also become rewritten. It is therefore not surprising that a large Magento store with numerous products and categories could have thousands upon thousands of rows within this table, especially when each URL is rewritten on a per-store basis. There are many configuration options within Magento that allow us to decide how and what Magento rewrites for us automatically. Another important point to note is that your category URL key may change depending on whether an existing category with the same URL key at the same level had existed previously in the system. If this situation occurs, an automatic incremental integer is appended to the URL key, for example, wooden-furniture-2.html. Magento Enterprise Edition has been enhanced to only allow unique URL keys. To know more, go to goo.gl/CKprNB. Optimizing our CMS pages CMS pages within Magento are primarily used as information pages. Terms and conditions, privacy policy, and returns policy are all examples of CMS pages that are created and configured within the Magento administration panel under CMS | Pages . By default, the home page of a Magento store is a CMS page with the title Home Page . The page that is served as the home page can be configured within the Magento Configuration under System | Configuration | Web | Default Pages . The most important part of a CMS page setup is that its URL key is always relative to the website's base URL. This means that when creating CMS pages, you can manually choose how deep you wish the page to exist on the site. This gives us the ability to create as many nested CMS pages as we like. Another important point to note is that, by default, CMS pages have no file extension (URL suffix) as opposed to the category and product URLs where we can specify which extension to use (if any). For CMS pages, the default optimization methods that are available to us are found within the Page Information tabs after selecting a CMS page: Under the Page Information subtab, we can choose our Page Title and URL key Under the Content subtab, we can enter our Content Heading (by default, this gets inserted into an <h1> tag) and enter our body content Under the Meta Data subtab, we can specify our keywords and description As mentioned previously, we would focus optimization on these pages purely for the intent of our users. If we were not using custom blocks or other methods to display product information, we would not optimize these information pages for keywords relating to purchasing a product. Summary In this article, we have learned the basic concepts of keyword placement and the roles of the different types of pages to prepare and configure your Magento website. Resources for Article : Further resources on this subject: Magento: Exploring Themes [Article] Magento : Payment and shipping method [Article] Integrating Twitter with Magento [Article]
Read more
  • 0
  • 0
  • 4032

article-image-creating-and-configuring-basic-mobile-application
Packt
17 Jan 2013
3 min read
Save for later

Creating and configuring a basic mobile application

Packt
17 Jan 2013
3 min read
(For more resources related to this topic, see here.) How to do it... Follow these steps: Inside your Magento Admin Panel, navigate to Mobile | Manage Apps on the main menu. Click on the Add App button in the top-right corner. The New App screen will be shown. Since we have to create a separate application for each mobile device type, let's choose our first targeted platform. Under the Device Type list, we can choose iPad, iPhone, or Android. For the purpose of this recipe, since the procedure is almost the same for all device types, I will choose Android. After choosing the desired Device Type, click on the Continue button, and click on the General tab under Manage Mobile App. First we have to fill in the box named App Name. Choose an appropriate name for your mobile application and insert it there. Under the Store View list, make sure to choose our earlier defined Store View with updated mobile theme exceptions, our mobile copyright information, and category thumbnail images. Set the Catalog Only App option to No. Click on the Save and Continue Edit button in the top-right corner of the screen. Now you will notice a warning message from Magento that says something like the following: Please upload an image for "Logo in Header" field from Design Tab. Please upload an image for "Banner on Home Screen" field from Design Tab. Don't worry, Magento expects us to add some basic images that we prepared for our mobile app. So let's add them. Click on the Design tab on the left-hand side of the screen. Locate the Logo in Header label and click on the Browse... button on the right to upload the prepared small header logo image. Make sure to upload the image with proper dimensions for the selected device type (iPhone, iPad, or Android). In the same way, click on the Browse... button on the right of the Banner on Home Screen label and choose the appropriate prepared and resized banner image. Now, let's click on the Save and Continue Edit button in order to save our settings. How it works For each device type, we will have to create a new Magento Mobile application in our Magento Mobile Admin Panel. When we once select Device Type and click on the Save button, we are unable to change Device Type later for that application. If we have chosen the wrong Device Type, the only solution is to delete this app and to create a new one with the proper settings. The same applies with our chosen Store View when configuring new app. There's more... When our configuration is saved for the first time, auto-generated App Code will appear on the screen and that will be the code which will uniquely identify our Device Type—the assigned application to be properly recognized with Magento Mobile. For example, defand1 means that this application is the first defined application for the default Store View targeted on android (def = default store view, and=android). How to use mobile application as catalog only Under step 7 we set Catalog Only App to No, but sometimes, if we don't need checkout and payment in our mobile app, but we want to use it just as catalog to show products to our mobile customers, we just need to set the Catalog Only option to Yes. Summary So this is how we create the basic configuration for our mobile app Resources for Article : Further resources on this subject: Integrating Twitter with Magento [Article] Integrating Facebook with Magento [Article] Getting Started with Magento Development [Article]
Read more
  • 0
  • 0
  • 4028
article-image-cakephp-authentication-setup-application
Packt
28 Mar 2011
11 min read
Save for later

CakePHP: authentication setup on an application

Packt
28 Mar 2011
11 min read
In this CakePHP tutorial, you'll learn how to set up a basic authentication system. Follow the recipes and you'll find all the code you need. Setting up a basic authentication system The first task to be completed when we are in the process of adding authentication to an application is to identify which controllers will need user access. Normally we would make every controller and action protected by default, and then we would specify which areas of our application allow public access. Getting ready We must have a users table that should contain, at least, two fields: username (to hold the username) and password (to hold a hash made out of the user's password). If you don't have a table for this purpose, you can use the following SQL statement to create it: CREATE TABLE `users`( `id` INT UNSIGNED AUTO_INCREMENT NOT NULL, `username` VARCHAR(255) NOT NULL, `password` CHAR(40) NOT NULL, PRIMARY KEY(`id`) ); How to do it... Create a file named users_controller.php and place it inside your app/controllers folder with the following contents: <?php class UsersController extends AppController { public function login() { } public function logout() { $this->redirect($this->Auth->logout()); } } ?> Create a file named login.ctp in your app/views/users folder (create the folder if you don't have one already), and add the following contents: <?php echo $this->Form->create(array('action'=>'login')); echo $this->Form->inputs(array( 'legend' => 'Login', 'username', 'password' )); echo $this->Form->end('Login'); ?> Create a file named app_controller.php in your app/ folder with the following contents: <?php class AppController extends Controller { public $components = array( 'Auth' => array( 'authorize' => 'controller' ), 'Session' ); public function isAuthorized() { return true; } } ?> Modify the UsersController, and add the following code before the login method: public function beforeFilter() { parent::beforeFilter(); $this->Auth->allow('add'); } public function add() { if (!empty($this->data)) { $this->User->create(); if ($this->User->save($this->data)) { $this->Session->setFlash('User created!'); $this->redirect(array('action'=>'login')); } else { $this->Session->setFlash('Please correct the errors'); } } } Create a file named add.ctp and place it in your app/views/users folder with the following contents: <?php echo $this->Form->create(); echo $this->Form->inputs(array( 'legend' => 'Signup', 'username', 'password' )); echo $this->Form->end('Submit'); ?> We now have a fully working authentication system. We can add new users by browsing to http://localhost/users/add, logging in by browsing to http://localhost/users/login, and finally logging out by browsing to http://localhost/users/logout. After creating a user, you should see the login form with a success message, as shown in the following screenshot: How it works... We start by creating two actions in the UsersController class: login(), to show and process submissions of the login form, and logout(), to handle users logging out. You may be surprised that the login() method has no logic whatsoever. To display the form, all we need to do is display the action's view. The form submission is taken care of by the Auth component, leaving us with no need to implement any controller logic. Therefore, the only implementation we need is to create a view for this action, which includes a simple form with two fields: username, and password. The inputs method of CakePHP's FormHelper is a shortcut designed to avoid multiple calls to the input method. By using it, we can create a full form with elements without the need to call FormHelper::input() several times. The logout() controller action simply calls the Auth component's logout() method. This method removes the logged-in user data from the session, and returns the address to which the user should be redirected after logging out, obtained from the previously configured logoutRedirect setting of the component (defaults to the application's home page if the setting was not configured.) Next, we add two components to the controller: Session, and Auth. The Session component is needed to create the messages (through the use of its setflash() method) that informs the user if a login attempt was unsuccessful, or if a user was created. The Auth component operates between your controller's actions and the incoming request by means of the beforeFilter callback method. It uses it's authorize setting to check what type of authentication scheme is to be used. Once the Auth component is added to a controller, all actions in that controller are not accessible unless there is a valid user logged in. This means that if we had any actions that should be public (such as the login() and add() actions in our controller), we would have to tell the Auth component about them. If one wishes to make some actions public, one can add the name of these actions to the allowedActions setting of the Auth component, or by calling its allow() method. We use the later approach to tell the Auth component that the add() action should be reachable without a logged-in user. The login() action is automatically added to the list of public actions by the Auth component. When the user attempts to reach an action that is not within the public actions, the Auth component checks the session to see if a user is already logged in. If a valid user is not found, it redirects the browser to the login action. If there is a user who is logged in, it uses the controller's isAuthorized method to check if the user has access. If its return value is true, it allows access, otherwise access is rejected. In our case, we implemented this method in AppController, our base controller class. If the attempted action requires a user who is logged in, the login() action is executed. After the user submits data using the login form, the component will first hash the password field, and then issue a find operation on the User model to find a valid account, using the posted username and password. If a valid record is found, it is saved to the session, marking the user as logged in. Hashing a password confirmation field When the Auth component is enabled on a controller and the user submits a form with a field named password (regardless if it is being rendered in the login form), the component will automatically hash the password field before executing the controller's action. The Auth component uses the salt defined in the configuration setting Security.salt (in your app/config/core.php file) to calculate the hash. Different salt values will produce different hashes even when using the same password. Therefore, make sure you change the salt on all your CakePHP applications, thus enhancing the security of your authentication system. This means that the action will never hold the plain password value, and this should be particularly noted when utilizing mechanisms to confirm password validations. When you are implementing such validation, make sure you hash the confirmation field using the proper method: if (!empty($this->data)) { $this->data['User']['confirm_password'] = $this->Auth- >password($this->data['User']['confirm_password']); // Continue with processing } Using and configuring the Auth component If there is something that defines the Auth component, it is its flexibility that accounts for different types of authentication modes, each of these modes serving different needs. In this recipe, you will learn how to modify the component's default behavior, and how to choose between the different authentications modes. Getting ready We should have a fully working authentication system, so follow the entire recipe Setting up a basic authentication system. We will also add support to have disabled user accounts. Add a field named active to your users table with the following SQL statement: ALTER TABLE `users` ADD COLUMN `active` TINYINT UNSIGNED NOT NULL default 1; How to do it... Modify the definition of the Auth component in your AppController class, so it looks like the following: public $components = array( 'Auth' => array( 'authorize' => 'controller', 'loginRedirect' => array( 'admin' => false, 'controller' => 'users', 'action' => 'dashboard' ), 'loginError' => 'Invalid account specified', 'authError' => 'You don't have the right permission' ), 'Session' ); Now while still editing your app/app_controller.php file, place the following code right below the components property declaration, at the beginning of the beforeFilter method in your AppController class: public function beforeFilter() { if ($this->Auth->getModel()->hasField('active')) {$this->Auth->userScope = array('active' => 1); } } Copy the default layout from cake/libs/view/layouts/default.ctp to your app/views/layouts directory, and make sure you place the following line in your layout where you wish to display authentication messages: <?php echo $this->Session->flash('auth'); ?> Edit your app/controllers/users_controller.php file and place the following method right below the logout() method: public function dashboard() { } Finally, create the view for this newly added action in a file named dashboard.ctp and place it in your app/views/users folder with the following contents: <p>Welcome!</p> If you now browse to http://localhost/users/login and enter the wrong credentials (wrong username and/or password), you should see the error message shown in the following screenshot: How it works... As the Auth component does its magic right before a controller action is executed, we either need to specify its settings in the beforeFilter callback, or pass them in an array when adding the component to the components property. A common place to do it is in the beforeFilter() method of the AppController class, as by doing so we can share the same authentication settings throughout all our controllers. This recipe changes some Auth settings, so that whenever a valid user logs in, they are automatically taken to a dashboard action in the UsersController (done via the loginRedirect setting.) It also adds some default error messages through the component's respective settings: loginError for when the given account is invalid, and authError for when there is a valid account, but the action is not authorized (which can be achieved by returning false from the isAuthorized() method implemented in AppController.) It also sets the component's userScope setting in AppController::beforeFilter(). This setting allows us to define which conditions the User find operation need to match to allow a user account to log in. By adding the userScope setting, we ensure that only user records that have the active field set to 1 are allowed access. Changing the default user model As you may have noticed, the role of the User model is crucial, not only to fetch the right user account, but also to check the permissions on some of the authentication schemes. By default, the Auth component will look for a User model, but you can change which model is to be used by setting the userModel property or the userModel key in the settings array. For example, if your user model is Account, you would add the following setting when adding the Auth component to your controller: 'userModel' => 'Account' Or equivalently, you would add the following to the beforeFilter method of your AppController class, in the block of code where you are setting up the component: $this->Auth->userModel = 'Account'; There's more... The $authorize property of the Auth component (or the authorize key in the Auth component settings array) defines which authentication scheme should be used. Possible values are: controller: It makes the component use the controller's isAuthorized method, which returns true to allow access, or false to reject it. This method is particularly useful when obtaining the logged-in user model: It is similar to controller; instead of using the controller to call the method, it looks for the isAuthorized method in the User model. First, it tries to map the controller's action to a CRUD operation (one of 'create', 'read', 'update', or 'delete'), and then calls the method with three arguments: the user record, the controller that is being accessed, and the CRUD operation (or actual controller action) that is to be executed. object: It is similar to model; instead of using the model to call the method, it looks for the isAuthorized method in a given class. In order to specify which class, set the AuthComponent::$object property to an instance of such a class. It calls the method with three arguments: the user record, the controller that is being accessed, and the action that is to be executed. actions: It uses the Acl component to check for access, which allows a much more grained access control. crud: It is similar to actions; the difference lies in the fact that it first tries to map the controller's action to a CRUD operation (one of 'create', 'read', 'update', or 'delete'.)
Read more
  • 0
  • 0
  • 4013

article-image-trunks-using-3cx-part-1
Packt
12 Feb 2010
11 min read
Save for later

Trunks using 3CX: Part 1

Packt
12 Feb 2010
11 min read
PSTN trunks A Public Switch Telephone Network (PSTN) trunk is an old fashioned analog Basic Rate Interface (BRI) ISDN or Primary Rate Interface (PRI) phone line. 3CX can use any of these with the correct analog to SIP gateway. Usually these come into your home or business through a pair of copper lines. Depending on where you live, this may be the only means of connecting 3CX and communicating outside of your network. One of the advantages of a PSTN line is reliability and great call quality. Unless the wires break, you will almost always have phone service. However, what about call quality? After all, many people would like to have a comparison between VoIP and PSTN. Analog hardware for BRI ISDN and PRI's will be discussed in greater detail in Chapter 9. For using an analog PSTN line, you will need an FXO gateway. There are many external ones available. Until Sangoma introduced a new line at the end of 2008, there had not been any gateway which worked inside a Windows PC with 3CX. There are many manufacturers of analog gateways such as Linksys, Audio-Codes, Patton Electronics, GrandStream, and Sangoma. What these FXO gateways do is convert the analog phone line into IP signaling. Then the IP signaling gets passed over your network to the 3CX server and your phones. My personal preference is Patton Electronics. They are probably the most expensive FXOs' out there, but in this case, you get what you pay for. I have tried all of them and they all work. Some have issues with echo which can be hard to get rid of without support, or lots of trial and error, whereas some cannot support high demands (40 calls/hour) without needing to be reset every day, so if you are just testing, get a low-end one. For a high demand business, my preference is Patton. Not only do they make great products, but their support is top notch too. We will configure a Patton SmartNode SN4114 later in this article. SIP trunks What is a SIP trunk? A SIP trunk is a call that is routed by IP over the Internet through an Internet Telephony Service Provider (ITSP). For enterprises wanting to make full use of their installed IP PBXs' and communicate over IP not only within the enterprise, but also outside the enterprise—a SIP trunk provided by an ITSP that connects to the traditional PSTN network is the solution. Unlike traditional telephony, where bundles of physical wires were once delivered from the service provider to a business, a SIP trunk allows a company to replace traditional fixed PSTN lines with PSTN connectivity via a SIP trunking service provider on the Internet. SIP trunks can offer significant cost savings for enterprises, eliminating the need for local PSTN gateways, costly ISDN BRI's or PRI's. The following figure is an example of how our phone system operates: You can see that we have a local area network containing our desktops, servers, phones, and our 3CX Phone System. To reach the outside world using a SIP trunk, we have to go through our firewall or router. Depending on your network, you could be using a private IP address (10.x.x.x, 172.16.x.x, or 192.168.x.x) which is not allowed on the public Internet, so it has to get translated to the public IP address. This translation process is called Network Address Translation (NAT). Once we get outside the local network, we are in the public realm. Our ITSP uses the internet to get our phone call to/from the various carriers PSTN (analog) lines where our phone call is connected/terminated. There are three components necessary to successfully deploy SIP trunks: A PBX with a SIP-enabled trunk side An enterprise edge device understanding SIP An Internet Telephony or SIP trunking service provider The PBX In most cases, the PBX is an IP-based PBX, communicating with all endpoints over IP. However, it may just as well be a traditional digital or analog PBX. The sole requirement that has to be available is an interface for SIP trunking connectivity. The enterprise border element The PBX on the LAN connects to the ITSP via the enterprise border element. The enterprise edge component can either be a firewall with complete support for SIP, or an edge device connected to the firewall handling the traversal of the SIP traffic. The ITSP On the Internet, the ITSP provides connectivity to the PSTN for communication with mobile and fixed phones. Choosing a VoIP carrier—more than just price I feel two of the most important features to look for when choosing a VoIP carrier is support and call quality. Usually once you setup and everything is working, you won't need support. I always tell clients that there is no "boxed" solution that I can sell, every installation is a little different. Internet connections are all different even with the same provider. If you have a rock-solid T1 or something better, then this shouldn't be a problem. DSL seems different from building to building, even in the same area. So how do you test support before giving them your credit card? Call them! Try calling support at the worst times such as Monday afternoons when everyone is back to work and online, also try calling after business hours. See how long does it take to connect to a live person and if you can understand them once you speak to them? Find where is their support located? Try talking to them and tell them you are thinking about signing up with their service and ask them for help. If they go out of their way before they have your money, chances are they will be good to work with later on. Some carriers only offer chat or email support in favor of lower prices. While this may work fine for your business, it certainly won't work for the ones who need answers right away. I know I seem to be stressing a lot on support but it's for good reason. If your business depends on phone service and it goes down then you need answers! I pay more for a product if the support is worth it. Part of this is your Return On Investment (ROI). For example, if you have 3 lawyers billing at $200/hour and they need phones to work, that's $600/hour of lost time. Does the extra $50 or $100 upfront cover that? Now back to the topic at hand. Once you have connected 3CX to the carrier, how is the call quality? If it sounds like a bad cell phone, you probably don't want it, unless the price is so cheap that you can live with the low quality. Certain carriers even change the way your call gets routed through the Internet, based on the lowest cost for the particular call. They don't care about quality as long as you get that connection and they make money on it. Concurrent calls with an ITSP are a feature that you may want to look for when choosing an ITSP. Some accounts are a one-to-one ratio of lines per call. If you want to have 5 people on the phone at the same time (inbound or outbound), you would need to pay for 5 lines, this is similar to a PSTN line. You may get some savings here over a PSTN but that depends on what is available in your area. Some ITSP's have concurrent calls where you can use more than one line per call. Not many carriers have this feature but for a small business, this can be a great cost saving feature to look for. I use a couple of different carriers that have this feature. One carrier that I use lets you have 3 concurrent calls simultaneously on the same line. If you need more than 3 calls, you're a higher use customer and they want you to buy several lines. VoIP IP signaling uses special algorithms to compress your voice into IP packets. This compression uses a codec. There are several available, but the most common one is G.711u-law or A-law. This uses about 80kpbs of upload and download bandwidth. Another popular codec is G.729, it uses about 36kpbs. So for the same bandwidth you can have twice the number of calls using G.729 than G.711. You will need to check with your ITSP and see what codec they support. Another carrier I use is based purely on how much internet bandwidth you have. If you have 1Mbps of upload speed (usually the slowest part of your internet connection), you can support about 10 simultaneous or concurrent calls using G.711. You then pay for the minutes you use. This works very well for a small office as your monthly bill is very low and you don't have to maintain a bunch of lines that don't get used. Cable internet providers are also offering VoIP service to your home or business. These are usually single-use lines but they terminate at your office with an FXS plug. To integrate this with 3CX, you will need an FXO just like it's a PSTN line, same setup but you get the advantage of a VoIP line. Another great benefit of a SIP trunk is expandability. You can easily start out with one line which can usually be completed in one day. As you grow you can add more, usually in minutes as you already have the plan setup. Time to consolidate lines? You can even drop them later on without having contracts (most of the time). Try doing that with the local phone company! Call for a new business and it can take 1-2 weeks to get set up, plus contracts to worry about. No wonder they are jumping on the VoIP band wagon. Disaster recovery What do you do when your internet goes down? Some of you might be saying, "Ha! It never goes down". In my experience, it will eventually, and at the worst time. So what do you do? Go home for the day or plan for a backup? Most VoIP carriers provide some kind of disaster recovery option. They try to send you a call and when they don't get a connection to your 3CX box; then then re-route the call to another phone number. This could be a PSTN line or even a cell phone. It can be a free feature or there can be a small monthly fee on the account. It's worth having, especially if you rely on phones. Okay, so that covers inbound disaster recovery. What about outbound? Yes just about everyone has a cell phone these days, if that isn't enough, I'd suggest you invest in a pay-per-use PSTN line. This keeps the monthly cost very low but it's there when you need it. Whether it's an emergency pizza order for that Friday afternoon party or a true emergency when someone panics and dials 911—you want that call to go out. Speaking of emergency numbers, make sure you have your carrier register that phone number to your local address. Let's say you are in New York and you have a Californian phone number to give you some local presence in that part of the country. Your co-worker grabs his chest and falls down and someone dials 911 from the closest phone they see. Emergency services see your Californian number and contacts California for help for your New York office, that's not what you want when someone is clutching their chest, even though it was just heartburn from that pepperoni pizza. Mixing VoIP and PSTN Some of my clients even mix VoIP and PSTN together. Why would you mix? Local calls and inbound calls use the PSTN lines for the best call quality (and do not use any VoIP minutes if they have to pay for those). Long distance calls use the cheaper rate VoIP line. Another scenario is using PSTN lines for all your incoming and outgoing calls and use VoIP to talk to your other offices. Your own office can deal with a lower call quality, and management will appreciate the lower cost. These types of setups can be controlled using a dial plan.
Read more
  • 0
  • 0
  • 3982
Modal Close icon
Modal Close icon