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
Videos
Audiobooks
Learning Hub
Newsletter Hub
Free Learning
Arrow right icon
timer SALE ENDS IN
0 Days
:
00 Hours
:
00 Minutes
:
00 Seconds

How-To Tutorials - Web Development

1802 Articles
article-image-building-customizable-content-management-system
Packt
07 Apr 2014
15 min read
Save for later

Building a Customizable Content Management System

Packt
07 Apr 2014
15 min read
(For more resources related to this topic, see here.) Mission briefing This article deals with the creation of a Content Management System. This system will consist of two parts: A backend that helps to manage content, page parts, and page structure A frontend that displays the settings and content we just entered We will start this by creating an admin area and then create page parts with types. Page parts, which are like widgets, are fragments of content that can be moved around the page. Page parts also have types; for example, we can display videos in our left column or display news. So, the same content can be represented in multiple ways. For example, news can be a separate page as well as a page part if it needs to be displayed on the front page. These parts need to be enabled for the frontend. If enabled, then the frontend makes a call on the page part ID and renders it in the part where it is supposed to be displayed. We will do a frontend markup in Haml and Sass. The following screenshot shows what we aim to do in this article: Why is it awesome? Everyone loves to get a CMS built from scratch that is meant to suit their needs really closely. We will try to build a system that is extremely simple as well as covers several different types of content. This system is also meant to be extensible, and we will lay the foundation stone for a highly configurable CMS. We will also spice up our proceedings in this article by using MongoDB instead of a relational database such as MySQL. At the end of this article, we will be able to build a skeleton for a very dynamic CMS. Your Hotshot objectives While building this application, we will have to go through the following tasks: Creating a separate admin area Creating a CMS with the ability of handling different types of content pages Managing page parts Creating a Haml- and Sass-based template Generating the content and pages Implementing asset caching Mission checklist We need to install the following software on the system before we start with our mission: Ruby 1.9.3 / Ruby 2.0.0 Rails 4.0.0 MongoDB Bootstrap 3.0 Haml Sass Devise Git A tool for mockups jQuery ImageMagick and RMagick Memcached Creating a separate admin area We have used devise for all our projects and we will be using the same strategy in this article. The only difference is that we will use it to log in to the admin account and manage the site's data. This needs to be done when we navigate to the URL/admin. We will do this by creating a namespace and routing our controller through the namespace. We will use our default application layout and assets for the admin area, whereas we will create a different set of layout and assets altogether for our frontend. Also, before starting with this first step, create an admin role using CanCan and rolify and associate it with the user model. We are going to use memcached for caching, hence we need to add it to our development stack. We will do this by installing it through our favorite package manager, for example, apt on Ubuntu: sudo apt-get install memcached Prepare for lift off In order to start working on this article, we will have to first add the mongoid gem to Gemfile: Gemfile gem 'mongoid'4', github: 'mongoid/mongoid' Bundle the application and run the mongoid generator: rails g mongoid:config You can edit config/mongoid.yml to suit your local system's settings as shown in the following code: config/mongoid.yml development: database: helioscms_development hosts: - localhost:27017 options: test: sessions: default: database: helioscms_test hosts: - localhost:27017 options: read: primary max_retries: 1 retry_interval: 0 We did this because ActiveRecord is the default Object Relationship Mapper (ORM). We will override it with the mongoid Object Document Mapper (ODM) in our application. Mongoid's configuration file is slightly different from the database.yml file for ActiveRecord. The session's rule in mongoid.yml opens a session from the Rails application to MongoDB. It will keep the session open as long as the server is up. It will also open the connection automatically if the server is down and it restarts after some time. Also, as a part of the installation, we need to add Haml to Gemfile and bundle it: Gemfile gem 'haml' gem "haml-rails" Engage thrusters Let's get cracking to create our admin area now: We will first generate our dashboard controller: rails g controller dashboard indexcreate app/controllers/dashboard_controller.rbroute get "dashboard/index"invoke erbcreate app/views/dashboardcreate app/views/dashboard/index.html.erbinvoke test_unitcreate test/controllers/dashboard_controller_test.rbinvoke helpercreate app/helpers/dashboard_helper.rbinvoke test_unitcreate test/helpers/dashboard_helper_test.rbinvoke assetsinvoke coffeecreate app/assets/javascripts/dashboard.js.coffeeinvoke scsscreate app/assets/stylesheets/dashboard.css.scss We will then create a namespace called admin in our routes.rb file: config/routes.rbnamespace :admin doget '', to: 'dashboard#index', as: '/'end We have also modified our dashboard route such that it is set as the root page in the admin namespace. Our dashboard controller will not work anymore now. In order for it to work, we will have to create a folder called admin inside our controllers and modify our DashboardController to Admin::DashboardController. This is to match the admin namespace we created in the routes.rb file: app/controllers/admin/dashboard_controller.rbclass Admin::DashboardController < ApplicationControllerbefore_filter :authenticate_user!def indexendend In order to make the login specific to the admin dashboard, we will copy our devise/sessions_controller.rb file to the controllers/admin path and edit it. We will add the admin namespace and allow only the admin role to log in: app/controllers/admin/sessions_controller.rbclass Admin::SessionsController < ::Devise::SessionsControllerdef createuser = User.find_by_email(params[:email])if user && user.authenticate(params[:password]) &&user.has_role? "admin"session[:user_id] = user.idredirect_to admin_url, notice: "Logged in!"elseflash.now.alert = "Email or password is invalid /Only Admin is allowed "endendend redirect_to admin_url, notice: "Logged in!" else flash.now.alert = "Email or password is invalid / Only Admin is allowed " end end end Objective complete – mini debriefing In the preceding task, after setting up devise and CanCan in our application, we went ahead and created a namespace for the admin. In Rails, the namespace is a concept used to separate a set of controllers into a completely different functionality. In our case, we used this to separate out the login for the admin dashboard and a dashboard page as soon as the login happens. We did this by first creating the admin folder in our controllers. We then copied our Devise sessions controller into the admin folder. For Rails to identify the namespace, we need to add it before the controller name as follows: class Admin::SessionsController < ::Devise::SessionsController In our route, we defined a namespace to read the controllers under the admin folder: namespace :admin doend We then created a controller to handle dashboards and placed it within the admin namespace: namnamespace :admin doget '', to: 'dashboard#index', as: '/'end We made the dashboard the root page after login. The route generated from the preceding definition is localhost:3000/admin. We ensured that if someone tries to log in by clicking on the admin dashboard URL, our application checks whether the user has a role of admin or not. In order to do so, we used has_role from rolify along with user.authenticate from devise: if user && user.authenticate(params[:password]) && user.has_role? "admin" This will make devise function as part of the admin dashboard. If a user tries to log in, they will be presented with the devise login page as shown in the following screenshot: After logging in successfully, the user is redirected to the link for the admin dashboard: Creating a CMS with the ability to create different types of pages A website has a variety of types of pages, and each page serves a different purpose. Some are limited to contact details, while some contain detailed information about the team. Each of these pages has a title and body. Also, there will be subpages within each navigation; for example, the About page can have Team, Company, and Careers as subpages. Hence, we need to create a parent-child self-referential association. So, pages will be associated with themselves and be treated as parent and child. Engage thrusters In the following steps, we will create page management for our application. This will be the backbone of our application. Create a model, view, and controller for page. We will have a very simple page structure for now. We will create a page with title, body, and page type: app/models/page.rbclass Pageinclude Mongoid::Documentfield :title, type: Stringfield :body, type: Stringfield :page_type, type: Stringvalidates :title, :presence => truevalidates :body, :presence => truePAGE_TYPE= %w(Home News Video Contact Team Careers)end We need a home page for our main site. So, in order to set a home page, we will have to assign it the type home. However, we need two things from the home page: it should be the root of our main site and the layout should be different from the admin. In order to do this, we will start by creating an action called home_page in pages_controller: app/models/page.rb scope :home, ->where(page_type: "Home")} app/controllers/pages_controller.rb def home_page @page = Page.home.first rescue nil render :layout => 'page_layout' end We will find a page with the home type and render a custom layout called page_layout, which is different from our application layout. We will do the same for the show action as well, as we are only going to use show to display the pages in the frontend: app/controllers/pages_controller.rbdef showrender :layout => 'page_layout'end Now, in order to effectively manage the content, we need an editor. This will make things easier as the user will be able to style the content easily using it. We will use ckeditor in order to style the content in our application: Gemfilegem "ckeditor", :github => "galetahub/ckeditor"gem 'carrierwave', :github => "jnicklas/carrierwave"gem 'carrierwave-mongoid', :require => 'carrierwave/mongoid'gem 'mongoid-grid_fs', github: 'ahoward/mongoid-grid_fs' Add the ckeditor gem to Gemfile and run bundle install: helioscms$ rails generate ckeditor:install --orm=mongoid--backend=carrierwavecreate config/initializers/ckeditor.rbroute mount Ckeditor::Engine => '/ckeditor'create app/models/ckeditor/asset.rbcreate app/models/ckeditor/picture.rbcreate app/models/ckeditor/attachment_file.rbcreate app/uploaders/ckeditor_attachment_file_uploader.rb This will generate a carrierwave uploader for CKEditor, which is compatible with mongoid. In order to finish the configuration, we need to add a line to application.js to load the ckeditor JavaScript: app/assets/application.js//= require ckeditor/init We will display the editor in the body as that's what we need to style: views/pages/_form.html.haml.field= f.label :body%br/= f.cktext_area :body, :rows => 20, :ckeditor => {:uiColor =>"#AADC6E", :toolbar => "mini"} We also need to mount the ckeditor in our routes.rb file: config/routes.rbmount Ckeditor::Engine => '/ckeditor' The editor toolbar and text area will be generated as seen in the following screenshot: In order to display the content on the index page in a formatted manner, we will add the html_safe escape method to our body: views/pages/index.html.haml%td= page.body.html_safe The following screenshot shows the index page after the preceding step: At this point, we can manage the content using pages. However, in order to add nesting, we will have to create a parent-child structure for our pages. In order to do so, we will have to first generate a model to define this relationship: helioscms$ rails g model page_relationship Inside the page_relationship model, we will define a two-way association with the page model: app/models/page_relationship.rbclass PageRelationshipinclude Mongoid::Documentfield :parent_idd, type: Integerfield :child_id, type: Integerbelongs_to :parent, :class_name => "Page"belongs_to :child, :class_name => "Page"end In our page model, we will add inverse association. This is to check for both parent and child and span the tree both ways: has_many :child_page, :class_name => 'Page',:inverse_of => :parent_pagebelongs_to :parent_page, :class_name => 'Page',:inverse_of => :child_page We can now add a page to the form as a parent. Also, this method will create a tree structure and a parent-child relationship between the two pages: app/views/pages/_form.html.haml.field= f.label "Parent"%br/= f.collection_select(:parent_page_id, Page.all, :id,:title, :class => "form-control").field= f.label :body%br/= f.cktext_area :body, :rows => 20, :ckeditor =>{:uiColor => "#AADC6E", :toolbar => "mini"}%br/.actions= f.submit :class=>"btn btn-default"=link_to 'Cancel', pages_path, :class=>"btn btn-danger" We can see the the drop-down list with names of existing pages, as shown in the following screenshot: Finally, we will display the parent page: views/pages/_form.html.haml.field= f.label "Parent"%br/= f.collection_select(:parent_page_id, Page.all, :id,:title, :class => "form-control") In order to display the parent, we will call it using the association we created: app/views/pages/index.html.haml- @pages.each do |page|%tr%td= page.title%td= page.body.html_safe%td= page.parent_page.title if page.parent_page Objective complete – mini debriefing Mongoid is an ODM that provides an ActiveRecord type interface to access and use MongoDB. MongoDB is a document-oriented database, which follows a no-schema and dynamic-querying approach. In order to include Mongoid, we need to make sure we have the following module included in our model: include Mongoid::Document Mongoid does not rely on migrations such as ActiveRecord because we do not need to create tables but documents. It also comes with a very different set of datatypes. It does not have a datatype called text; it relies on the string datatype for all such interactions. Some of the different datatypes are as follows: Regular expressions: This can be used as a query string, and matching strings are returned as a result Numbers: This includes integer, big integer, and float Arrays: MongoDB allows the storage of arrays and hashes in a document field Embedded documents: This has the same datatype as the parent document We also used Haml as our markup language for our views. The main goal of Haml is to provide a clean and readable markup. Not only that, Haml significantly reduces the effort of templating due to its approach. In this task, we created a page model and a controller. We added a field called page_type to our page. In order to set a home page, we created a scope to find the documents with the page type home: scope :home, ->where(page_type: "Home")} We then called this scope in our controller, and we also set a specific layout to our show page and home page. This is to separate the layout of our admin and pages. The website structure can contain multiple levels of nesting, which means we could have a page structure like the following: About Us | Team | Careers | Work Culture | Job Openings In the preceding structure, we were dealing with a page model to generate different pages. However, our CMS should know that About Us has a child page called Careers and in turn has another child page called Work Culture. In order to create a parent-child structure, we need to create a self-referential association. In order to achieve this, we created a new model that holds a reference on the same model page. We first created an association in the page model with itself. The line inverse_of allows us to trace back in case we need to span our tree according to the parent or child: has_many :child_page, :class_name => 'Page', :inverse_of => :parent_pagebelongs_to :parent_page, :class_name => 'Page', :inverse_of =>:child_page We created a page relationship to handle this relationship in order to map the parent ID and child ID. Again, we mapped it to the class page: belongs_to :parent, :class_name => "Page"belongs_to :child, :class_name => "Page" This allowed us to directly find parent and child pages using associations. In order to manage the content of the page, we added CKEditor, which provides a feature rich toolbar to format the content of the page. We used the CKEditor gem and generated the configuration, including carrierwave. For carrierwave to work with mongoid, we need to add dependencies to Gemfile: gem 'carrierwave', :github => "jnicklas/carrierwave" gem 'carrierwave-mongoid', :require => 'carrierwave/mongoid' gem 'mongoid-grid_fs', github: 'ahoward/mongoid-grid_fs' MongoDB comes with its own filesystem called GridFs. When we extend carrierwave, we have an option of using a filesystem and GridFs, but the gem is required nonetheless. carrierwave and CKEditor are used to insert and manage pictures in the content wherever required. We then added a route to mount the CKEditor as an engine in our routes file. Finally, we called it in a form: = f.cktext_area :body, :rows => 20, :ckeditor => {:uiColor =>"#AADC6E", :toolbar => "mini"} CKEditor generates and saves the content as HTML. Rails sanitizes HTML by default and hence our HTML is safe to be saved. The admin page to manage the content of pages looks like the following screenshot:
Read more
  • 0
  • 0
  • 1751

article-image-organizing-jade-projects
Packt
24 Mar 2014
9 min read
Save for later

Organizing Jade Projects

Packt
24 Mar 2014
9 min read
(For more resources related to this topic, see here.) Now that you know how to use all the things that Jade can do, here's when you should use them. Jade is pretty flexible when it comes to organizing projects; the language itself doesn't impose much structure on your project. However, there are some conventions you should follow, as they will typically make your code easier to manage. This article will cover those conventions and best practices. General best practices Most of the good practices that are used when writing HTML carry over to Jade. Some of these include the following: Using a consistent naming convention for ID's, class names, and (in this case) mixin names and variables Adding alt text to images Choosing appropriate tags to describe content and page structure The list goes on, but these are all things you should already be familiar with. So now we're going to discuss some practices that are more Jade-specific. Keeping logic out of templates When working with a templating language, like Jade, that allows you to use advanced logical operations, separation of concerns (SoC) becomes an important practice. In this context, SoC is the separation of business and presentational logic, allowing each part to be developed and updated independently. An easy point to draw the border between business and presentation is where data is passed to the template. Business logic is kept in the main code of your application and passes the data to be presented (as well-formed JSON objects) to your template engine. From there, the presentation layer takes the data and performs whatever logic is needed to make that data into a readable web page. An additional advantage of this separation is that the JSON data can be passed to a template over stdio (to the server-side Jade compiler), or it can be passed over TCP/IP (to be evaluated client side). Since the template only formats the given data, it doesn't matter where it is rendered, and can be used on both server and client. For documenting the format of the JSON data, try JSON Schema (http://json-schema.org/). In addition to describing the interface between that your presentation layer uses, it can be used in tests to validate the structure of the JSON that your business layer produces. Inlining When writing HTML, it is commonly advised that you don't use inline styles or scripts because it is harder to maintain. This advice still applies to the way you write your Jade. For everything but the smallest one-page projects, tests, and mockups, you should separate your styles and scripts into different files. These files may then be compiled separately and linked to your HTML with style or link tags. Or, you could include them directly into the Jade. But either way, the important part is that you keep it separated from your markup in your source code. However, in your compiled HTML you don't need to worry about keeping inlined styles out. The advice about avoiding inline styles applies only to your source code and is purely for making your codebase easier to manage. In fact, according to Best Practices for Speeding Up Your Web Site (http://developer.yahoo.com/performance/rules.html) it is much better to combine your files to minimize HTTP requests, so inlining at compile time is a really good idea. It's also worth noting that, even though Jade can help you inline scripts and styles during compilation, there are better ways to perform these compile-time optimizations. For example, build-tools like AssetGraph (https://github.com/assetgraph/assetgraph) can do all the inlining, minifying, and combining you need, without you needing to put code to do so in your templates. Minification We can pass arguments through filters to compilers for things like minifying. This feature is useful for small projects for which you might not want to set up a full build-tool. Also, minification does reduce the size of your assets making it a very easy way to speed up your site. However, your markup shouldn't really concern itself with details like how the site is minified, so filter arguments aren't the best solution for minifying. Just like inlining, it is much better to do this with a tool like AssetGraph. That way your markup is free of "build instructions". Removing style-induced redundancy A lot of redundant markup is added just to make styling easier: we have wrappers for every conceivable part of the page, empty divs and spans, and plenty of other forms of useless markup. The best way to deal with this stuff is to improve your CSS so it isn't reliant on wrappers and the like. Failing that, we can still use mixins to take that redundancy out of the main part of our code and hide it away until we have better CSS to deal with it. For example, consider the following example that uses a repetitive navigation bar: input#home_nav(type='radio', name='nav', value='home', checked) label(for='home_nav') a(href='#home') home input#blog_nav(type='radio', name='nav', value='blog') label(for='blog_nav') a(href='#blog') blog input#portfolio_nav(type='radio', name='nav', value='portfolio') label(for='portfolio_nav') a(href='#portfolio') portfolio //- ...and so on Instead of using the preceding code, it can be refactored into a reusable mixin as shown in the following code snippet: mixin navbar(pages) - checked = true for page in pages input( type='radio', name='nav', value=page, id="#{page}_nav", checked=checked) label(for="#{page}_nav") a(href="##{page}") #{page} - checked = false The preceding mixin can be then called later in your markup using the following code: +navbar(['home', 'blog', 'portfolio']) Semantic divisions Sometimes, even though there is no redundancy present, dividing templates into separated mixins and blocks can be a good idea. Not only does it provide encapsulation (which makes debugging easier), but the division represents a logical separation of the different parts of a page. A common example of this would be dividing a page between the header, footer, sidebar, and main content. These could be combined into one monolithic file, but putting each in a separate block represents their separation, can make the project easier to navigate, and allows each to be extended individually. Server-side versus client-side rendering Since Jade can be used on both the client-side and server-side, we can choose to do the rendering of the templates off the server. However, there are costs and benefits associated with each approach, so the decision must be made depending on the project. Client-side rendering Using the Single Page Application (SPA) design, we can do everything but the compilation of the basic HTML structure on the client-side. This allows for a static page that loads content from a dynamic backend and passes that content to Jade templates compiled for client-side usage. For example, we could have simple webapp that, once loaded, fires off a AJAX request to a server running WordPress with a simple JSON API, and displays the posts it gets by passing the the JSON to templates. The benefits of this design is that the page itself is static (and therefore easily cacheable), with the SPA design, navigation is much faster (especially if content is preloaded), and significantly less data is transferred because of the terse JSON format that the content is formatted in (rather than it being already wrapped in HTML). Also, we get a very clean separation of content and presentation by actually forcing content to be moved into a CMS and out of the codebase. Finally, we avoid the risk of coupling the rendering too tightly with the CMS by forcing all content to be passed over HTTP in JSON—in fact, they are so separated that they don't even need to be on the same server. But, there are some issues too—the reliance on JavaScript for loading content means that users who don't have JS enabled will not be able to load content normally and search engines will not be able to see your content without implementing _escaped_fragment_ URLs. Thus, some fallback is needed, whether it is a full site that is able to function without JS or just simple HTML snapshots rendered using a headless browser, it is a source of additional work. Server-side rendering We can, of course, render everything on the server-side and just send regular HTML to the browser. This is the most backwards compatible, since the site will behave just as any static HTML site would, but we don't get any of the benefits of client-side rendering either. We could still use some client-side Jade for enhancements, but the idea is the same: the majority gets rendered on the server-side and full HTML pages need to be sent when the user navigates to a new page. Build systems Although the Jade compiler is fully capable of compiling projects on its own, in practice, it is often better to use a build system because they can make interfacing with the compiler easier. In addition, build systems often help automate other tasks such as minification, compiling other languages, and even deployment. Some examples of these build systems are Roots (http://roots.cx/), Grunt (http://gruntjs.com/), and even GNU's Make (http://www.gnu.org/software/make/). For example, Roots can recompile Jade automatically each time you save it and even refresh an in-browser preview of that page. Continuous recompilation helps you notice errors sooner and Roots helps you avoid the hassle of manually running a command to recompile. Summary In this article, we just finished taking a look at some of the best practices to follow when organizing Jade projects. Also, we looked at the use of third-party tools to automate tasks. Resources for Article: Further resources on this subject: So, what is Node.js? [Article] RSS Web Widget [Article] Cross-browser-distributed testing [Article]
Read more
  • 0
  • 0
  • 2134

article-image-article-phone-calls-send-sms-your-website-using-twilio
Packt
21 Mar 2014
9 min read
Save for later

Make phone calls and send SMS messages from your website using Twilio

Packt
21 Mar 2014
9 min read
(For more resources related to this topic, see here.) Sending a message from a website Sending messages from a website has many uses; sending notifications to users is one good example. In this example, we're going to present you with a form where you can enter a phone number and message and send it to your user. This can be quickly adapted for other uses. Getting ready The complete source code for this recipe can be found in the Chapter6/Recipe1/ folder. How to do it... Ok, let's learn how to send an SMS message from a website. The user will be prompted to fill out a form that will send the SMS message to the phone number entered in the form. Download the Twilio Helper Library from https://github.com/twilio/twilio-php/zipball/master and unzip it. Upload the Services/ folder to your website. Upload config.php to your website and make sure the following variables are set: <?php $accountsid = ''; // YOUR TWILIO ACCOUNT SID $authtoken = ''; // YOUR TWILIO AUTH TOKEN $fromNumber = ''; // PHONE NUMBER CALLS WILL COME FROM ?> Upload a file called sms.php and add the following code to it: <!DOCTYPE html> <html> <head> <title>Recipe 1 – Chapter 6</title> </head> <body> <?php include('Services/Twilio.php'); include("config.php"); include("functions.php"); $client = new Services_Twilio($accountsid, $authtoken); if( isset($_POST['number']) && isset($_POST['message']) ){ $sid = send_sms($_POST['number'],$_POST['message']); echo "Message sent to {$_POST['number']}"; } ?> <form method="post"> <input type="text" name="number" placeholder="Phone Number...." /><br /> <input type="text" name="message" placeholder="Message...." /><br /> <button type="submit">Send Message</button> </form> </body> </html> Create a file called functions.php and add the following code to it: <?php function send_sms($number,$message){ global $client,$fromNumber; $sms = $client->account->sms_messages->create( $fromNumber, $number, $message ); return $sms->sid; } How it works... In steps 1 and 2, we downloaded and installed the Twilio Helper Library for PHP. This library is the heart of your Twilio-powered apps. In step 3, we uploaded config.php that contains our authentication information to talk to Twilio's API. In steps 4 and 5, we created sms.php and functions.php, which will send a message to the phone number we enter. The send_sms function is handy for initiating SMS conversations; we'll be building on this function heavily in the rest of the article. Allowing users to make calls from their call logs We're going to give your user a place to view their call log. We will display a list of incoming calls and give them the option to call back on these numbers. Getting ready The complete source code for this recipe can be found in the Chapter9/Recipe4 folder. How to do it... Now, let's build a section for our users to log in to using the following steps: Update a file called index.php with the following content: <?php session_start(); include 'Services/Twilio.php'; require("system/jolt.php"); require("system/pdo.class.php"); require("system/functions.php"); $_GET['route'] = isset($_GET['route']) ? '/'.$_GET['route'] : '/'; $app = new Jolt('site',false); $app->option('source', 'config.ini'); #$pdo = Db::singleton(); $mysiteURL = $app->option('site.url'); $app->condition('signed_in', function () use ($app) { $app->redirect( $app->getBaseUri().'/login',!$app->store('user')); }); $app->get('/login', function() use ($app){ $app->render( 'login', array(),'layout' ); }); $app->post('/login', function() use ($app){ $sql = "SELECT * FROM `user` WHERE `email`='{$_POST['user']}' AND `password`='{$_POST['pass']}'"; $pdo = Db::singleton(); $res = $pdo->query( $sql ); $user = $res->fetch(); if( isset($user['ID']) ){ $_SESSION['uid'] = $user['ID']; $app->store('user',$user['ID']); $app->redirect( $app->getBaseUri().'/home'); }else{ $app->redirect( $app->getBaseUri().'/login'); } }); $app->get('/signup', function() use ($app){ $app->render( 'register', array(),'layout' ); }); $app->post('/signup', function() use ($app){ $client = new Services_Twilio($app->store('twilio.accountsid'), $app->store('twilio.authtoken') ); extract($_POST); $timestamp = strtotime( $timestamp ); $subaccount = $client->accounts->create(array( "FriendlyName" => $email )); $sid = $subaccount->sid; $token = $subaccount->auth_token; $sql = "INSERT INTO 'user' SET `name`='{$name}',`email`='{$email }',`password`='{$password}',`phone_number`='{$phone_number}',`sid` ='{$sid}',`token`='{$token}',`status`=1"; $pdo = Db::singleton(); $pdo->exec($sql); $uid = $pdo->lastInsertId(); $app->store('user',$uid ); // log user in $app->redirect( $app->getBaseUri().'/phone-number'); }); $app->get('/phone-number', function() use ($app){ $app->condition('signed_in'); $user = $app->store('user'); $client = new Services_Twilio($user['sid'], $user['token']); $app->render('phone-number'); }); $app->post("search", function() use ($app){ $app->condition('signed_in'); $user = get_user( $app->store('user') ); $client = new Services_Twilio($user['sid'], $user['token']); $SearchParams = array(); $SearchParams['InPostalCode'] = !empty($_POST['postal_code']) ? trim($_POST['postal_code']) : ''; $SearchParams['NearNumber'] = !empty($_POST['near_number']) ? trim($_POST['near_number']) : ''; $SearchParams['Contains'] = !empty($_POST['contains'])? trim($_ POST['contains']) : '' ; try { $numbers = $client->account->available_phone_numbers->getList('US', 'Local', $SearchParams); if(empty($numbers)) { $err = urlencode("We didn't find any phone numbers by that search"); $app->redirect( $app->getBaseUri().'/phone-number?msg='.$err); exit(0); } } catch (Exception $e) { $err = urlencode("Error processing search: {$e->getMessage()}"); $app->redirect( $app->getBaseUri().'/phone-number?msg='.$err); exit(0); } $app->render('search',array('numbers'=>$numbers)); }); $app->post("buy", function() use ($app){ $app->condition('signed_in'); $user = get_user( $app->store('user') ); $client = new Services_Twilio($user['sid'], $user['token']); $PhoneNumber = $_POST['PhoneNumber']; try { $number = $client->account->incoming_phone_numbers->create(array( 'PhoneNumber' => $PhoneNumber )); $phsid = $number->sid; if ( !empty($phsid) ){ $sql = "INSERT INTO numbers (user_id,number,sid) VALUES('{$u ser['ID']}','{$PhoneNumber}','{$phsid}');"; $pdo = Db::singleton(); $pdo->exec($sql); $fid = $pdo->lastInsertId(); $ret = editNumber($phsid,array( "FriendlyName"=>$PhoneNumber, "VoiceUrl" => $mysiteURL."/voice?id=".$fid, "VoiceMethod" => "POST", ),$user['sid'], $user['token']); } } catch (Exception $e) { $err = urlencode("Error purchasing number: {$e->getMessage()}"); $app->redirect( $app->getBaseUri().'/phone-number?msg='.$err); exit(0); } $msg = urlencode("Thank you for purchasing $PhoneNumber"); header("Location: index.php?msg=$msg"); $app->redirect( $app->getBaseUri().'/home?msg='.$msg); exit(0); }); $app->route('/voice', function() use ($app){ }); $app->get('/transcribe', function() use ($app){ }); $app->get('/logout', function() use ($app){ $app->store('user',0); $app->redirect( $app->getBaseUri().'/login'); }); $app->get('/home', function() use ($app){ $app->condition('signed_in'); $uid = $app->store('user'); $user = get_user( $uid ); $client = new Services_Twilio($user['sid'], $user['token']); $app->render('dashboard',array( 'user'=>$user, 'client'=>$client )); }); $app->get('/delete', function() use ($app){ $app->condition('signed_in'); }); $app->get('/', function() use ($app){ $app->render( 'home' ); }); $app->listen(); Upload a file called dashboard.php with the following content to your views folder: <h2>My Number</h2> <?php $pdo = Db::singleton(); $sql = "SELECT * FROM `numbers` WHERE `user_ id`='{$user['ID']}'"; $res = $pdo->query( $sql ); while( $row = $res->fetch() ){ echo preg_replace("/[^0-9]/", "", $row['number']); } try { ?> <h2>My Call History</h2> <p>Here are a list of recent calls, you can click any number to call them back, we will call your registered phone number and then the caller</p> <table width=100% class="table table-hover tabled-striped"> <thead> <tr> <th>From</th> <th>To</th> <th>Start Date</th> <th>End Date</th> <th>Duration</th> </tr> </thead> <tbody> <?php foreach ($client->account->calls as $call) { # echo "<p>Call from $call->from to $call->to at $call->start_time of length $call->duration</p>"; if( !stristr($call->direction,'inbound') ) continue; $type = find_in_list($call->from); ?> <tr> <td><a href="<?=$uri?>/call?number=<?=urlencode($call->from)?>"><?=$call->from?></a></td> <td><?=$call->to?></td> <td><?=$call->start_time?></td> <td><?=$call->end_time?></td> <td><?=$call->duration?></td> </tr> <?php } ?> </tbody> </table> <?php } catch (Exception $e) { echo 'Error: ' . $e->getMessage(); } ?> <hr /> <a href="<?=$uri?>/delete" onclick="return confirm('Are you sure you wish to close your account?');">Delete My Account</a> How it works... In step 1, we updated the index.php file. In step 2, we uploaded dashboard.php to the views folder. This file checks if we're logged in using the $app->condition('signed_in') method, which we discussed earlier, and if we are, it displays all incoming calls we've had to our account. We can then push a button to call one of those numbers and whitelist or blacklist them. Summary Thus in this article we have learned how to send messages and make phone calls from your website using Twilio. Resources for Article: Further resources on this subject: Make phone calls, send SMS from your website using Twilio [article] Trunks in FreePBX 2.5 [article] Trunks using 3CX: Part 1 [article]
Read more
  • 0
  • 0
  • 16111

article-image-getting-started-cmis
Packt
19 Mar 2014
9 min read
Save for later

Getting Started with CMIS

Packt
19 Mar 2014
9 min read
(For more resources related to this topic, see here.) What is CMIS? The goal of CMIS is to provide a standard method for accessing content from different content repositories. Using CMIS service calls, it is possible to navigate through and create content in a repository. CMIS also includes a query language for searching both the metadata and full-text content stored that is stored in a repository. The CMIS standard defines the protocols and formats for the requests and responses of API service calls made to a repository. CMIS acts as a standard interface and protocol for accessing content repositories, something similar to how ANSI-SQL acts as a common-denominator language for interacting with different databases. The use of the CMIS API for accessing repositories brings with it a number of benefits. Perhaps chief among these is the fact that access to CMIS is language neutral. Any language that supports HTTP services can be used to access a CMIS-enabled repository. Client software can be written to use a single API and be deployed to run against multiple CMIS-compliant repositories. Alfresco and CMIS The original draft for CMIS 0.5 was written by EMC, IBM and Microsoft. Shortly after that draft, Alfresco and other vendors joined the CMIS standards group. Alfresco was an early CMIS adopter and offered an implementation of CMIS version 0.5 in 2008. In 2009, Alfresco began hosting an on-line preview of the CMIS standard. The server, accessible via the http://cmis.alfresco.com URL, still exists and implements the latest CMIS standard. As of this writing, that URL hosts a preview of CMIS 1.1 features. In mid-2010, just after the CMIS 1.0 standard was approved, Alfresco released CMIS in both the Alfresco Community and Enterprise editions. In 2012, with Alfresco version 4.0, Alfresco moved from a home grown CMIS runtime implementation to one that uses the Apache Chemistry OpenCMIS Server Framework. From that release, developers have been able to customize Alfresco using the OpenCMIS Java API. Overview of the CMIS Standard Next we discuss the details of the CMIS specification, particularly the domain model, the different services that it provides, and the supported protocol bindings. Domain model (Object model) Every content repository vendor has their own definition of a content or object model. Alfresco, for example, has rich content modeling capabilities, such as types and aspects that can inherit from other types and aspects, and properties that can be assigned attributes like data-type, multi-valued and required. But there are wide differences in the ways in which different vendors have implemented content modeling. In the Documentum ECM system, for example, the generic content type is called dm_document, while in Alfresco it is called cm:content. Another example is the concept of an aspect as used in Alfresco – many repositories do not support that idea. The CMIS Domain Model is an attempt by the CMIS Standardization group to define a framework generic enough that can describe content models and map to concepts used by many different repository vendors. The CMIS Domain Model defines a Repository as a container and an entry point to all content items, from now on called objects. All objects are classified by an Object Type , which describes a common set of Properties (like Type ID, Parent, and Display Name). There are five base types of objects: Document, Folder, Relationship, Policy , Item(available from CMIS 1.1), and these all inherit from Object Type. In addition to the five base object types there are also a number of property types that can be used when defining new properties for an Object type. These are shown in the figure: String, Boolean, Decimal, Integer, and DateTime. Besides these property types there are also the URI, Id, and HTML property types, not shown in the figure. Taking a closer look at each one of the base types, we can see that: Document almost always corresponds to a file, although it need not have any content (when you upload a file via, for example, the AtomPub binding the metadata is created with the first request and the content for the file is posted with the second request). Folder is a container for file-able objects such as folders and documents. Immediately after filing a folder or document into a folder, an implicit parent-child relationship is automatically created. The fileable property of the object type definition specifies whether an object is file-able or not. Relationship object defines a relationship between a target and source object. Objects can have multiple relationships with other objects. The support for relationship objects is optional. Policy is a way of defining administrative policies to manage objects. An object to which a policy may be applied is called a controllable object (controllablePolicy property has to be set to true). For example, a CMIS policy could be used to define a retention policy. A policy is opaque and has no meaning to the repository. It must be implemented and enforced in a repository-specific way. For example, rules might be used in Alfresco to enforce a policy. The support for policy objects is optional. Item (CMIS 1.1) object represents a generic type of a CMIS information asset. For example, this could be a user or group object. Item objects are not versionable and do not have content streams like documents, but they do have properties like all other CMIS objects. The support for item objects is optional. Additional object types can be defined in a repository as custom subtypes of the base types. The Legal Case type shown in the figure above is an example. CMIS services are provided for the discovery of object types that are defined in a repository. However, object type management services, such as the creation, modification, and deletion of an object type, are not covered by the CMIS standard. An object has one primary base object type, such as Document or Folder, which cannot be changed. An object can also have secondary object types applied to it (CMIS 1.1). A secondary type is a named class that may add extra properties to an object in addition to the properties defined by the object's primary base object-type (This is similar to the concept of aspects in Alfresco). Every CMIS object has an opaque and immutable Object Identity (ID), which is assigned by the repository when the object is created. In the case of Alfresco, a Node Reference is created which becomes the Object ID. The ID uniquely identifies an object within a repository regardless of the type of the object. All CMIS objects have a set of named, but not explicitly ordered, properties. Within an object, each property is uniquely identified by its Property ID. In addition, a document object can have a Content Stream, which is then used to hold the actual byte content from a file. A document can also have one or more Renditions, like a thumbnail, a different sized image, or an alternate representation of the content stream. Document or folder objects can have one Access Control List (ACL), which controls access to the document or folder. An ACL is made up of a list of Access Control Entries (ACEs). An ACE in turn represents one or more permissions being granted to a principal, such as a user, group, role, or something similar. All objects and properties are defined in the cmis name-space. From now on we will refer to the different objects and properties via their fully qualified name, for example cmis:document or cmis:name. Services The following CMIS services can access and manage CMIS objects in the repository: Repository Services: These are used to discover information about the repository, including repository ids (more than one repository could be managed by the endpoint). Since many features are optional, this provides a way to find out which are supported. CMIS 1.1 compliant repositories also support the creation of new types dynamically. Methods: getRepositories, getRepositoryInfo , getTypeChildren, getTypeDescendants , getTypeDefinition , createType (CMIS 1.1), updateType (CMIS 1.1), deleteType (CMIS 1.1) Navigation Services: These are used to navigate the folder hierarchy. Methods: getChildren, getDescendants, getFolderTree, getFolderParent, getObjectParents, getCheckedOutDocs. Object Services: These services provide ID-based CRUD (Create, Read, Update, Delete) operations. Methods: createDocument, createDocumentFromSource, createFolder, createRelationship, createPolicy, createItem (CMIS 1.1), getAllowableActions, getObject, getProperties, getObjectByPath, getContentStream, getRenditions, updateProperties, bulkUpdateProperties (CMIS 1.1), moveObject, deleteObject, deleteTree, setContentStream, appendContentStream (CMIS 1.1), deleteContentStream. Multi-filing Services: These services (optional) makes it possible to put an object to several folders (multi-filing) or outside the folder hierarchy (un-filing). This service is not used to create or delete objects. Methods: addObjectToFolder, removeObjectFromFolder. Discovery Services: These are used to search for query-able objects within the Repository (objects with property queryable set to true ). Methods: query , getContentChanges. Versioning Services: These are used to manage versioning of document objects, other objects are not versionable. Whether or not a document can be versioned is controlled by the versionable property in the Object type. Methods: checkOut, cancelCheckOut, checkIn, getObjectOfLatestVersion, getPropertiesOfLatestVersion, getAllVersions. Relationship Services: These (optional) are used to retrieve the relationships in which an object is participating. Methods: getObjectRelationships. Policy Services: These (optional) are used to apply or remove a policy object to an object which has the property controllablePolicy set to true. Methods: applyPolicy, removePolicy, getAppliedPolicies. ACL Services: This service is used to discover and manage the Access Control List (ACL) for an object, if the object has one. Methods: applyACL, and getACL Summary In this article, we introduced the CMIS standard and how it came about. Then we covered the CMIS domain model with its five base object types: document, folder, relationship, policy, and item (CMIS 1.1.). We also learned that the CMIS standard defines a number of services, such as navigation and discovery, which makes it possible to manipulate objects in a content management system repository. Resources for Article: Further resources on this subject: Content Delivery in Alfresco 3 [Article] Getting Started with the Alfresco Records Management Module [Article] Managing Content in Alfresco [Article]
Read more
  • 0
  • 0
  • 12502

article-image-services
Packt
19 Mar 2014
12 min read
Save for later

Services

Packt
19 Mar 2014
12 min read
(For more resources related to this topic, see here.) Services A service is just a specific instance of a given class. For example, whenever you access doctrine such as $this->get('doctrine'); in a controller, it implies that you are accessing a service. This service is an instance of the Doctrine EntityManager class, but you never have to create this instance yourself. The code needed to create this entity manager is actually not that simple since it requires a connection to the database, some other configurations, and so on. Without this service already being defined, you would have to create this instance in your own code. Maybe you will have to repeat this initialization in each controller, thus making your application messier and harder to maintain. Some of the default services present in Symfony2 are as follows: The annotation reader Assetic—the asset management library The event dispatcher The form widgets and form factory The Symfony2 Kernel and HttpKernel Monolog—the logging library The router Twig—the templating engine It is very easy to create new services because of the Symfony2 framework. If we have a controller that has started to become quite messy with long code, a good way to refactor it and make it simpler will be to move some of the code to services. We have described all these services starting with "the" and a singular noun. This is because most of the time, services will be singleton objects where a single instance is needed. A geolocation service In this example, we imagine an application for listing events, which we will call "meetups". The controller makes it so that we can first retrieve the current user's IP address, use it as basic information to retrieve the user's location, and only display meetups within 50 kms of distance to the user's current location. Currently, the code is all set up in the controller. As it is, the controller is not actually that long yet, it has a single method and the whole class is around 50 lines of code. However, when you start to add more code, to only list the type of meetups that are the user's favorites or the ones they attended the most. When you want to mix that information and have complex calculations as to which meetups might be the most relevant to this specific user, the code could easily grow out of control! There are many ways to refactor this simple example. The geocoding logic can just be put in a separate method for now, and this will be a good step, but let's plan for the future and move some of the logic to the services where it belongs. Our current code is as follows: use GeocoderHttpAdapterCurlHttpAdapter; use GeocoderGeocoder; use GeocoderProviderFreeGeoIpProvider; public function indexAction()   { Initialize our geocoding tools (based on the excellent geocoding library at http://geocoder-php.org/) using the following code: $adapter = new CurlHttpAdapter(); $geocoder = new Geocoder(); $geocoder->registerProviders(array( new FreeGeoIpProvider($adapter), )); Retrieve our user's IP address using the following code: $ip = $this->get('request')->getClientIp(); // Or use a default one     if ($ip == '127.0.0.1') {   $ip = '114.247.144.250'; } Get the coordinates and adapt them using the following code so that they are roughly a square of 50 kms on each side: $result = $geocoder->geocode($ip); $lat = $result->getLatitude(); $long = $result->getLongitude(); $lat_max = $lat + 0.25; // (Roughly 25km) $lat_min = $lat - 0.25; $long_max = $long + 0.3; // (Roughly 25km) $long_min = $long - 0.3; Create a query based on all this information using the following code: $em = $this->getDoctrine()->getManager(); $qb = $em->createQueryBuilder(); $qb->select('e')     ->from('KhepinBookBundle:Meetup, 'e')     ->where('e.latitude < :lat_max')     ->andWhere('e.latitude > :lat_min')     ->andWhere('e.longitude < :long_max')     ->andWhere('e.longitude > :long_min')     ->setParameters([       'lat_max' => $lat_max,       'lat_min' => $lat_min,       'long_max' => $long_max,       'long_min' => $long_min     ]); Retrieve the results and pass them to the template using the following code: $meetups = $qb->getQuery()->execute(); return ['ip' => $ip, 'result' => $result, 'meetups' => $meetups]; } The first thing we want to do is get rid of the geocoding initialization. It would be great to have all of this taken care of automatically and we would just access the geocoder with: $this->get('geocoder');. You can define your services directly in the config.yml file of Symfony under the services key, as follows: services:   geocoder:     class: GeocoderGeocoder That is it! We defined a service that can now be accessed in any of our controllers. Our code now looks as follows: // Create the geocoding class $adapter = new GeocoderHttpAdapterCurlHttpAdapter(); $geocoder = $this->get('geocoder'); $geocoder->registerProviders(array(     new GeocoderProviderFreeGeoIpProvider($adapter), )); Well, I can see you rolling your eyes, thinking that it is not really helping so far. That's because initializing the geocoder is a bit more complex than just using the new GeocoderGeocoder() code. It needs another class to be instantiated and then passed as a parameter to a method. The good news is that we can do all of this in our service definition by modifying it as follows: services:     # Defines the adapter class     geocoder_adapter:         class: GeocoderHttpAdapterCurlHttpAdapter         public: false     # Defines the provider class     geocoder_provider:         class: GeocoderProviderFreeGeoIpProvider         public: false         # The provider class is passed the adapter as an argument         arguments: [@geocoder_adapter]     geocoder:         class: GeocoderGeocoder         # We call a method on the geocoder after initialization to set up the         # right parameters         calls:             - [registerProviders, [[@geocoder_provider]]] It's a bit longer than this, but it is the code that we never have to write anywhere else ever again. A few things to notice are as follows: We actually defined three services, as our geocoder requires two other classes to be instantiated. We used @+service_name to pass a reference to a service as an argument to another service. We can do more than just defining new Class($argument); we can also call a method on the class after it is instantiated. It is even possible to set properties directly when they are declared as public. We marked the first two services as private. This means that they won't be accessible in our controllers. They can, however, be used by the Dependency Injection Container (DIC) to be injected into other services. Our code now looks as follows: // Retrieve current user's IP address $ip = $this->get('request')->getClientIp(); // Or use a default one if ($ip == '127.0.0.1') {     $ip = '114.247.144.250'; } // Find the user's coordinates $result = $this->get('geocoder')->geocode($ip); $lat = $result->getLatitude(); // ... Remaining code is unchanged Here, our controllers are extending the BaseController class, which has access to DIC since it implements the ContainerAware interface. All calls to $this->get('service_name') are proxied to the container that constructs (if needed) and returns the service. Let's go one step further and define our own class that will directly get the user's IP address and return an array of maximum and minimum longitude and latitudes. We will create the following class: namespace KhepinBookBundleGeo; use GeocoderGeocoder; use SymfonyComponentHttpFoundationRequest; class UserLocator {     protected $geocoder;     protected $user_ip;     public function__construct(Geocoder $geocoder, Request $request) {         $this->geocoder = $geocoder;         $this->user_ip = $request->getClientIp();         if ($this->user_ip == '127.0.0.1') {             $this->user_ip = '114.247.144.250';         }     }     public function getUserGeoBoundaries($precision = 0.3) {         // Find the user's coordinates         $result = $this->geocoder->geocode($this->user_ip);         $lat = $result->getLatitude();         $long = $result->getLongitude();         $lat_max = $lat + 0.25; // (Roughly 25km)         $lat_min = $lat - 0.25;         $long_max = $long + 0.3; // (Roughly 25km)         $long_min = $long - 0.3;         return ['lat_max' => $lat_max, 'lat_min' => $lat_min,            'long_max' => $long_max, 'long_min' => $long_min];     } } It takes our geocoder and request variables as arguments, and then does all the heavy work we were doing in the controller at the beginning of the article. Just as we did before, we will define this class as a service, as follows, so that it becomes very easy to access from within the controllers: # config.yml services:     #...     user_locator:        class: KhepinBookBundleGeoUserLocator        scope: request        arguments: [@geocoder, @request] Notice that we have defined the scope here. The DIC has two scopes by default: container and prototype, to which the framework also adds a third one named request. The following table shows their differences: Scope Differences Container All calls to $this->get('service_name') return the sameinstance of the service. Prototype Each call to $this->get('service_name') returns a new instance of the service. Request Each call to $this->get('service_name') returns the same instance of the service within a request. Symfony can have subrequests (such as including a controller in Twig). Now, the advantage is that the service knows everything it needs by itself, but it also becomes unusable in contexts where there are no requests. If we wanted to create a command that gets all users' last-connected IP address and sends them a newsletter of the meetups around them on the weekend, this design would prevent us from using the KhepinBookBundleGeoUserLocator class to do so. As we see, by default, the services are in the container scope, which means they will only be instantiated once and then reused, therefore implementing the singleton pattern. It is also important to note that the DIC does not create all the services immediately, but only on demand. If your code in a different controller never tries to access the user_locator service, then that service and all the other ones it depends on (geocoder, geocoder_provider, and geocoder_adapter) will never be created. Also, remember that the configuration from the config.yml is cached when on a production environment, so there is also little to no overhead in defining these services. Our controller looks a lot simpler now and is as follows: $boundaries = $this->get('user_locator')->getUserGeoBoundaries(); // Create our database query $em = $this->getDoctrine()->getManager(); $qb = $em->createQueryBuilder(); $qb->select('e')     ->from('KhepinBookBundle:Meetup', 'e')     ->where('e.latitude < :lat_max')     ->andWhere('e.latitude > :lat_min')     ->andWhere('e.longitude < :long_max')     ->andWhere('e.longitude > :long_min')     ->setParameters($boundaries); // Retrieve interesting meetups $meetups = $qb->getQuery()->execute(); return ['meetups' => $meetups]; The longest part here is the doctrine query, which we could easily put on the repository class to further simplify our controller. As we just saw, defining and creating services in Symfony2 is fairly easy and inexpensive. We created our own UserLocator class, made it a service, and saw that it can depend on our other services such as @geocoder service. We are not finished with services or the DIC as they are the underlying part of almost everything related to extending Symfony2. Summary In this article, we saw the importance of services and also had a look at the geolocation service. We created a class, made it a service, and saw how it can depend on our other services. Resources for Article: Further resources on this subject: Developing an Application in Symfony 1.3 (Part 1) [Article] Developing an Application in Symfony 1.3 (Part 2) [Article] User Interaction and Email Automation in Symfony 1.3: Part1 [Article]
Read more
  • 0
  • 0
  • 1672

article-image-getting-started-bootstrap
Packt
14 Mar 2014
5 min read
Save for later

Getting Started with Bootstrap

Packt
14 Mar 2014
5 min read
(For more resources related to this topic, see here.) Why use Bootstrap? Bootstrap contains a top-notch, responsive mobile-first grid, which allows you to implement your design in a breeze; it comes with ready-made styles for typography, navigation, tables, forms, buttons, and more. Bootstrap also includes some jQuery plugins, such as Modal, Dropdown, Tooltip, and Carousel, which come in handy quite often. Today, you can use Bootstrap to throw together quick prototypes or guide the execution of more sophisticated designs and larger engineering efforts. In other words, Bootstrap is a very simple way to promote quick, clean and highly usable applications. – Mark Otto, creator of Bootstrap Even though Bootstrap comes with all these features, none of them actually get in the way of further customization. Bootstrap is very easy to extend, especially if you use LESS instead of traditional CSS. At its core, Bootstrap is just CSS, but it's built with Less, a flexible pre-processor that offers much more power and flexibility than regular CSS. With Less, we gain a range of features like nested declarations, variables, mixins, operations, and color functions. – Mark Otto, creator of Bootstrap Next, you will learn about the advantages and disadvantages of using Bootstrap. Bootstrap pros and cons As with many things, using Bootstrap too has its pros and cons. Let us list some important things that you will need to know when you decide whether or not to use Bootstrap in your project. The pros are as follows: Cross-browser support: Bootstrap works on all the latest desktop and mobile browsers. While older browsers may display Bootstrap differently with respect to styles, it is still fully functional in legacy browsers such as Internet Explorer 8. Easy to customize: Bootstrap is easy to customize, especially with the use of LESS. You can also leave out parts that you do not need, that is, you can use only its grid and leave out all the components, or you can leave out the grid and use its components. Encourages using LESS : Bootstrap is written in LESS, a dynamic style sheet language that is compiled into CSS, which gives it a lot of flexibility. You can take advantage of this if you use LESS to write your styles. Supports useful jQuery plugins: Bootstrap comes with many useful jQuery plugins that can come handy in many situations. The quality of the plugins is not the best, and they usually work best when you do not customize them at all. Many custom jQuery plugins available: There is a wide range of jQuery plugins that extend Bootstrap, for example, X-editable, Wysihtml5, and the jQuery File Upload. Mobile-first: Bootstrap has been mobile-first since Version 3.0. This means that the grid starts out stacked and is floated using media queries when the screen width grows. The cons are as follows: jQuery plugins are hard to customize : The jQuery plugins that come with Bootstrap are often hard to customize, and many argue that they are not written using best practices, so it can be challenging to work with the source code at times. Usually, the plugins work in the most common cases but they come up short when you try to customize them a bit. Many Bootstrap sites end up looking alike: It is unfortunate that many sites that are built with Bootstrap look exactly the same, but you can avoid this by using a custom theme or creating your own theme. Creating your first Bootstrap project Now that you know when it is suitable to use Bootstrap, you are ready to start your first Bootstrap project. Perform the following steps to get started: Create a new folder for your Bootstrap project inside your document root. You can call it bootstrap-app. Pick up the latest version of Bootstrap from http://getbootstrap.com and unpack it into your project directory. Create a new HTML document, add the following contents, and save it in your project directory as index.html in the following manner: <!DOCTYPE html> <html> <head> <title>Hello from Bootstrap</title> <!-- Ensure proper rendering and touch zooming on mobile devices --> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link href="css/bootstrap.min.css" rel="stylesheet"> <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries --> <!--[if lt IE 9]> <script src ="https://oss.maxcdn.com/libs/html5shiv/3.7.0/ html5shiv.js"> </script> <script src ="https://oss.maxcdn.com/libs/respond.js/1.3.0/ respond.min.js"> </script> <![endif]--> </head> <body> <h1>Hello, world!</h1> </body> </html> You can omit html5shiv.js and respond.js if you don't wish to support older versions of Internet Explorer. Let us look at the following reasons why we included all those CSS and JavaScript files: bootstrap.min.css: It is the minified version of the Bootstrap CSS styles html5shiv.js: It adds HTML5 support to older browsers respond.min.js: It adds media query support to older browsers Navigate to your project directory using your favorite web browser; you should see your project in action as shown in the following screenshot. Not too impressive, but do not worry, you will soon add more to it. For more information on how to get started with Bootstrap, refer to the Getting started page on the official site at http://getbootstrap.com/getting-started/. Summary In this article, you learned about the pros and cons of Bootstrap, as well as how to decide whether or not to use Bootstrap in a project. You also learned how to create a very simple Bootstrap project. Resources for Article: Further resources on this subject: Bootstrap 3.0 is Mobile First [Article] Downloading and setting up Bootstrap [Article] Top Features You Need to Know About – Responsive Web Design [Article]
Read more
  • 0
  • 0
  • 9950
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 $19.99/month. Cancel anytime
article-image-form-handling
Packt
20 Feb 2014
22 min read
Save for later

Form Handling

Packt
20 Feb 2014
22 min read
(For more resources related to this topic, see here.) Collecting user data is a basic function of many websites and web applications, from simple data collection techniques such as registration or login information, to more complex scenarios such as payment or billing information. It is important that only relevant and complete information is collected from the user. To ensure this, the web developer must enforce validation on all data input. It is also important to provide a good user experience while enforcing this data integrity. This can be done by providing useful feedback to the user regarding any validation errors their data may have caused. This article will show you how to create an attractive web form that enforces data integrity while keeping a high-quality user experience. A very important point to note is that any JavaScript or jQuery validation is open to manipulation by the user. JavaScript and jQuery resides within the web browser, so a user with little knowledge can easily modify the code to bypass any client-side validation techniques. This means that client-side validation cannot be totally relied on to prevent the user from submitting invalid data. Any validation done within the client side must be replicated on the server, which is not open for manipulation by the user. We use client-side validation to improve the user experience. Because of this, the user does not need to wait for a server response. Implementing basic form validation At the most basic level of form validation, you will need to be able to prevent the user from submitting empty values. This recipe will provide the HTML and CSS code for a web form that will be used for recipes 1 through 8 of this article. Getting ready Using your favorite text editor or IDE, create a blank HTML page in an easily accessible location and save this file as recipe-1.html. Ensure that you have the latest version of jQuery downloaded to the same location as this HTML file. This HTML page will form the basis of most of this article, so remember to keep it after you have completed this recipe. How to do it… Learn how to implement basic form validation with jQuery by performing the following steps: Add the following HTML code to index.html. Be sure to change the source location of the JavaScript included for the jQuery library, pointing it to where the latest version of jQuery is downloaded on your computer. <!DOCTYPE html> <html > <head>    <title>Chapter 5 :: Recipe 1</title>    <link type="text/css" media="screen" rel="stylesheet" href="styles.css" />    <script src = "jquery.min.js"></script>    <script src = "validation.js"></script> </head> <body>    <form id="webForm" method="POST">       <div class="header">          <h1>Register</h1>       </div>       <div class="input-frame">          <label for="firstName">First Name:</label>          <input name="firstName" id="firstName" type="text"             class="required" />       </div>       <div class="input-frame">          <label for="lastName">Last Name:</label>          <input name="lastName" id="lastName" type="text"             class="required" />       </div>       <div class="input-frame">          <label for="email">Email:</label>          <input name="email" id="email" type="text"             class="required email" />       </div>       <div class="input-frame">          <label for="number">Telephone:</label>          <input name="number" id="number" type="text"             class="number" />       </div>       <div class="input-frame">          <label for="dob">Date of Birth:</label>          <input name="dob" id="dob" type="text"             class="required date"             placeholder="DD/MM/YYYY"/>       </div>       <div class="input-frame">          <label for="creditCard">Credit Card #:</label>          <input name="creditCard" id="creditCard"             type="text" class="required credit-card" />       </div>       <div class="input-frame">          <label for="password">Password:</label>          <input name="password" id="password"             type="password" class="required" />       </div>       <div class="input-frame">          <label for="confirmPassword">Confirm             Password:</label>             <input name="confirmPassword"                id="confirmPassword" type="password"                class="required" />       </div>       <div class="actions">          <button class="submit-btn">Submit</button>       </div>    </form> </body> </html> Create a CSS file named styles.css in the same directory and add the following CSS code to add style to our HTML page and form: @import url(http: //fonts.googleapis.com/css?family=Ubuntu); body {    background-color: #FFF;    font-family: 'Ubuntu', sans-serif; } form {    width: 500px;    padding: 20px;    background-color: #333;    border-radius: 5px;    margin: 10px auto auto auto;    color: #747474;    border: solid 2px #000; } form label {    font-size: 14px;    line-height: 30px;    width: 27%;    display: inline-block;    text-align: right; } .input-frame {    clear: both;    margin-bottom: 25px;    position: relative; } form input {    height: 30px;    width: 330px;    margin-left: 10px;    background-color: #191919;    border: solid 1px #404040;    padding-left: 10px;    color: #DB7400; } form input:hover {    background-color: #262626; } form input:focus {    border-color: #DB7400; } form .header {    margin: -20px -20px 25px -20px;    padding: 10px 10px 10px 20px;    position: relative;    background-color: #DB7400;    border-top-left-radius: 4px;    border-top-right-radius: 4px; } form .header h1 {    line-height: 50px;    margin: 0px;    padding: 0px;    color: #FFF;    font-weight: normal; } .actions {    text-align: right; } .submit-btn {    background-color: #DB7400;    border: solid 1px #000;    border-radius: 5px;    color: #FFF;    padding: 10px 20px 10px 20px;    text-decoration: none;    cursor: pointer; } .error input {    border-color: red; } .error-data {    color: red;    font-size: 11px;    position: absolute;    bottom: -15px;    left: 30%; } In addition to the jQuery library, the previous HTML page also uses another JavaScript file. Create a blank JavaScript file in the directory where the index.html file is saved. Save this file as validation.js and add the following JavaScript code: $(function(){    $('.submit-btn').click(function(event){       //Prevent form submission       event.preventDefault();       var inputs = $('input');       var isError = false;       //Remove old errors       $('.input-frame').removeClass('error');       $('.error-data').remove();       for (var i = 0; i < inputs.length; i++) {          var input = inputs[i];          if ($(input).hasClass('required') &&             !validateRequired($(input).val())) {             addErrorData($(input), "This is a required             field");             isError = true;          }         }       if (isError === false) {          //No errors, submit the form          $('#webForm').submit();       }    }); });   function validateRequired(value) {    if (value == "") return false;    return true; }   function addErrorData(element, error) {    element.parent().addClass("error");    element.after("<div class='error-data'>" + error + "</div>"); } Open index.html in a web browser and you should see a form similar to the following screenshot: If you click on the Submit button to submit an empty form, you will be presented with error messages under the required fields. How it works… Now, let us understand the steps performed previously in detail. HTML The HTML creates a web form with various fields that will take a range of data inputs, including text, date of birth, and credit card number. This page forms the basis for most of this article. Each of the input elements has been given different classes depending on what type of validation they require. For this recipe, our JavaScript will only look at the required class, which indicates a required field and therefore cannot be blank. Other classes have been added to the input fields, such as date and number, which will be used in the later recipes in this article. CSS Basic CSS has been added to create an attractive web form. The CSS code styles the input fields so they blend in with the form itself and adds a hover effect. The Google Web Font Ubuntu has also been used to improve the look of the form. jQuery The first part of the jQuery code is wrapped within $(function(){});, which will ensure the code is executed on page load. Inside this wrapper, we attach a click event handler to the form submit button, shown as follows: $(function(){     $('.submit-btn').click(function(event){         //Prevent form submission         event.preventDefault();             }); }); As we want to handle the form submission based on whether valid data has been provided, we use event.preventDefault(); to initially stop the form from submitting, allowing us to perform the validation first, shown as follows: var inputs = $('input'); var isError = false; After the preventDefault code, an inputs variable is declared to hold all the input elements within the page, using $('input') to select them. Additionally, we create an isError variable, setting it to false. This will be a flag to determine if our validation code has discovered an error within the form. These variable declarations are shown previously. Using the length of the inputs variable, we are able to loop through all of the inputs on the page. We create an input variable for each input that is iterated over, which can be used to perform actions on the current input element using jQuery. This is done with the following code: for (var i = 0; i < inputs.length; i++) { var input = inputs[i]; } After the input variable has been declared and assigned the current input, any previous error classes or data is removed from the element using the following code: $(input).parent().removeClass('error'); $(input).next('.error-data').remove(); The first line removes the error class from the input's parent (.input-frame), which adds the red border to the input element. The second line removes the error information that is displayed under the input if the validation check has determined that this input has invalid data. Next, jQuery's hasClass() function is used to determine if the current input element has the required class. If the current element does have this class, we need to perform the required validation to make sure this field contains data. We call the validateRequired() function within the if statement and pass through the value of the current input, shown as follows: if ($(input).hasClass('required') && !validateRequired($(input).val())) { addErrorData($(input), "This is a required field");    isError = true; } We call the validateRequired() function prepended with an exclamation mark to check to determine if this function's results are equal to false; therefore, if the current input has the required class and validateRequired() returns false, the value of the current input is invalid. If this is the case, we call the addErrorData() function inside the if statement with the current input and the error message, which will be displayed under the input. We also set the isError variable to true, so that later on in the code, we will know a validation error occurred. The JavaScript's for loop will repeat these steps for each of the selected input elements on the page. After the for loop has completed, we check if the isError flag is still set to false. If so, we use jQuery to manually submit the form, shown as follows: if (isError === false) {    //No errors, submit the form    $('#webForm').submit(); } Note that the operator === is used to compare the variable type of isError (that is, Boolean) as well as its value. At the bottom of the JavaScript file, we declare our two functions that have been called earlier in the script. The first function, validateRequired(), simply takes the input value and checks to see if it is blank or not. If the value is blank, the function returns false, meaning validation failed; otherwise, the function returns true. This can be coded as follows: function validateRequired(value) {     if (value == "") return false;     return true; } The second function used is the addErrorData() function, which takes the current input and an error message. It uses jQuery's addClass() function to add the error class to the input's parent, which will display the red border on the input element using CSS. It then uses jQuery's after() function to insert a division element into the DOM, which will display the specified error message under the current input field, shown as follows: function validateRequired(value) {    if (value == "") return false;    return true; } function addErrorData(element, error) {    element.parent().addClass("error");    element.after("<div class='error-data'>" + error + "</div>"); } There's more... This structure allows us to easily add additional validation to our web form. Because the JavaScript is iterating over all of the input fields in the form, we can easily check for additional classes, such as date, number, and credit-card, and call extra functions to provide the alternative validation. The other recipes in this article will look in detail at the additional validation types and add these functions to the current validation.js file. See also Implementing input character restrictions Adding number validation When collecting data from a user, there are many situations when you will want to only allow numbers in a form field. Examples of this could be telephone numbers, PIN codes, or ZIP codes, to name a few. This recipe will show you how to validate the telephone number field within the form we created in the previous recipe. Getting ready Ensure that you have completed the previous recipe and have the same files available. Open validation.js in your text editor or IDE of choice. How to do it… Add number validation to the form you created in the previous recipe by performing the following steps: Update validation.js to be as follows, adding the valdiateNumber() function with an additional hasClass('number') check inside the for loop: $(function(){    $('.submit-btn').click(function(event){       //Prevent form submission       event.preventDefault();       var inputs = $('input');       var isError = false;       //Remove old errors       $('.input-frame').removeClass('error');       $('.error-data').remove();       for (var i = 0; i < inputs.length; i++) {          var input = inputs[i];            if ($(input).hasClass('required') &&             !validateRequired($(input).val())) {                addErrorData($(input), "This is a required                   field");                isError = true;             } /* Code for this recipe */          if ($(input).hasClass('number') &&             !validateNumber($(input).val())) {                addErrorData($(input), "This field can only                   contain numbers");                isError = true;             } /* --- */         }       if (isError === false) {          //No errors, submit the form          $('#webForm').submit();       }    }); });   function validateRequired(value) {    if (value == "") return false;    return true; }   /* Code for this recipe */ function validateNumber(value) {    if (value != "") {       return !isNaN(parseInt(value, 10)) && isFinite(value);       //isFinite, in case letter is on the end    }    return true; } /* --- */ function addErrorData(element, error) {    element.parent().addClass("error");    element.after("<div class='error-data'>" + error + "</div>"); } Open index.html in a web browser, input something other than a valid integer into the telephone number field, and click on the Submit button. You will be presented with a form similar to the following screenshot: How it works… First, we add an additional if statement to the main for loop of validation.js to check to see if the current input field has the class number, as follows: if ($(input).hasClass('number') &&    !validateNumber($(input).val())) {    addErrorData($(input), "This field can only contain numbers");    isError = true; } If it does, this input value needs to be validated for a number. To do this, we call the validateNumber function inline within the if statement: function validateNumber(value) {    if (value != "") {       return !isNaN(parseInt(value, 10)) && isFinite(value);       //isFinite, in case letter is on the end    }    return true; } This function takes the value of the current input field as an argument. It first checks to see if the value is blank. If it is, we do not need to perform any validation here because this is handled by the validateRequired() function from the first recipe of this article. If there is a value to validate, a range of actions are performed on the return statement. First, the value is parsed as an integer and passed to the isNaN() function. The JavaScript isNaN() function simply checks to see if the provided value is NaN (Not a Number). In JavaScript, if you try to parse a value as an integer and that value is not actually an integer, you will get the NaN value. The first part of the return statement is to ensure that the provided value is a valid integer. However, this does not prevent the user from inputting invalid characters. If the user was to input 12345ABCD, the parseInt function would ignore ABCD and just parse 12345, and therefore the validation would pass. To prevent this situation, we also use the isFinite function, which returns false if provided with 12345ABCD. Adding credit card number validation Number validation could be enough validation for a credit card number; however, using regular expressions, it is possible to check for number combinations to match credit card numbers from Visa, MasterCard, American Express, and more. Getting ready Make sure that you have validation.js from the previous two recipes in this article open and ready for modification. How to do it… Use jQuery to provide form input validation for credit card numbers by performing the following step-by-step instructions: Update validation.js to add the credit card validation function and the additional class check on the input fields: $(function(){    $('.submit-btn').click(function(event){       //Prevent form submission       event.preventDefault();       var inputs = $('input');       var isError = false;       for (var i = 0; i < inputs.length; i++) {   // -- JavaScript from previous two recipes hidden                       if ($(input).hasClass('credit-card') &&             !validateCreditCard($(input).val())) {             addErrorData($(input), "Invalid credit card                number");             isError = true;          }         } // -- JavaScript from previous two recipes hidden    }); });   // -- JavaScript from previous two recipes hidden   function validateCreditCard(value) {    if (value != "") {       return /^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6(?:011|5[0-9][0-9]) [0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}| (?:2131|1800|35d{3})d{11})$/.test(value);    }    return true; } // -- JavaScript from previous two recipes hidden } Open index.html and input an invalid credit card number. You will be presented with the following error information in the form: How it works… To add credit card validation, as with the previous two recipes, we added an additional check in the main for loop to look for the credit-card class on the input elements, as follows: if ($(input).hasClass('credit-card') &&    !validateCreditCard($(input).val())) {    addErrorData($(input), "Invalid credit card number");    isError = true; } The validateCreditCard function is also added, which uses a regular expression to validate the input value, as follows: function validateCreditCard(value) {    if (value != "") {       return /^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-          9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-          9]{13}|3(?:0[0-5]|[68][0-9])[0-          9]{11}|(?:2131|1800|35d{3})d{11})$/.test(value);    }    return true; } The first part of this function determines if the provided value is blank. If it isn't, the function will perform further validation; otherwise, it will return true. Most credit card numbers start with a prefix, which allows us to add additional validation to the inputted value on top of numeric validation. The regular expression used in this function will allow for Visa, MasterCard, American Express, Diners Club, Discover, and JCB cards. See also Adding number validation
Read more
  • 0
  • 0
  • 2025

article-image-baritem-class-and-radoutlookbar-control
Packt
18 Feb 2014
6 min read
Save for later

The BarItem class and the RadOutlookBar control

Packt
18 Feb 2014
6 min read
(For more resources related to this topic, see here.) The next aspect of the system we want to review is the BarItem class. The design of this class is set up to be used by the RadOutlookBarItem and RadMenuItem classes. The reason for this setup was to match the properties of the Telerik item classes. The following is a screenshot of the class design: The main properties include Header, Position, and ReferId. The Header property is set up to match the Header property from the RadOutlookBarItem and RadmenuItem classes. The Position property orders the items on the system for proper display, and the ReferId property creates the structure of the view. The ReferId values are set up so that if the ReferId property is zero, this item becomes the header, otherwise the rest of the items become the items under that main item. Now that we've covered the new concepts within this article, let's start to work with the navigation containers for Telerik. The next sections of this article will cover the RadOutlookBar and RadMenu controls using the concepts from this section to load these controls based on the level of user access. RadOutlookBar The first control I will review in this article will be the RadOutlookBar control. This control is unique to the Telerik toolset. The standard Visual Studio control library does not contain a control like the RadOutlookBar control. The first example I will review will be to set the binding of the control's tabs using both a list of class objects from the object layer, and the database to populate the list of objects and authenticate the user. The first step will be to create a new window in the current WPF project; name this new window Chap5OutlookWindow.xaml. Once you have created the new window, you will need to add the RadOutlookBar control: RadOutlookBar: Name the outlook bar RadTestOutlook The final appearance of the window should be the following screenshot: Notice that the RadOutlookBar control is already loaded with the options. This is the fully functioning example, but gives you an idea for how the window should look once we've finished this section. RadOutlook with GenericList, DataBinding, and database security This is the first example of populating the RadOutlookBar with the menu links from the database. The concept we are demonstrating is that the menu links inside the RadOutlookBar control will be populated based on the user that is authenticated into the system. In this example, we will be using database security. This means that the database will store the username and password, and security link information for the RadOutlookBar control. Since we don't have a login form to gather the username and password, we will create the user information within the App.xaml.cs file instead. This will allow us to simulate the login process and authenticate the user in the system. This should not be done for a production application—this setup is for demonstration purposes only. Hard-coding a username and password is always a bad practice in a real-world application. The following screenshot shows how the code should look inside the App.xaml.cs file: Let's review the preceding code. The first step is to create an instance of the UserEntity class. The next step is to determine the type of security the system will be using to authenticate the user. The app.config file has a setting for the type of security as shown in the following line of code: <add key="SecurityType" value="DB"/> This key value is passed to the system, and used by the system to determine which type of security is used in the App.xaml.cs class. The value is read by the system; it sets an enum property from the UserEntity class called SecurityType. This enumeration is then evaluated to determine the security setup for the system. The next step is to set the UserName and UserChkPwd properties. Once these properties are set we can then call the Authenticate method to verify the user information against the database. The next step will be to set the CurrentUser property in the application request class. This object is passed to each window to persist base application information to each window. The final step will be to create an instance of the OutlookBarWindow class to open the window. Notice the code for the instance; we are passing the AppRequest object to the window using the instance method of the window. The following screenshot is the instance code: Now that the window code is set up, let's review the LoadRadOutlook method from the BaseWindow class. This method requires two values: the OutlookBar object to be loaded, and a generic list of BarItem objects. The BarItem class takes the information from the database and loads the RadOutlookBar control using BarItems to create the RadOutlookBarItem objects to load the RadOutlookBar control. The LoadRadOutlook method also creates a RadTreeView control of the options within each RadOutlookBarItem object. The database version then handles the gathering of the BarItem class and loading the RadOutlookBar control using a method called FetchMenuOptions. This method takes BarItems from the database query, and created a RadOutlookBar object with all the menu options for the current user. This method gathers the menu information based on the current username and generates a generic list of the BarItem classes. This list is used by the BaseWindow class to load the RadOutlookBar control. RadOutlookBar using generic list binding with XML security The next method for populating the RadOutlookBar control on the window will be to use a serialized XML to populate the generic list of objects to load the RadOutlookBar control. The XML file has the group information and pulls the BarItem objects based on the user's group information. The example we will be using has the group information hard-coded into the group list, but this is just an example. You should not hard-code the groups in a real-world application. Here is the method for gathering the menu items from the XML file: Notice that we first load the XDocument object, then deserialize the XDocument object to gather the list of BarItem objects. Once the full list is loaded, we use a Linq query to gather the items based on the user group for the current user. Now that we have that new list, we can populate the UserMenuItems property for use in the application. The application then calls the LoadOutlookBar method to generate the RadOutlookBar control again. Summary Thus in this article we learned what a RadOutlookBar control is and how it can be used with GenericList, DataBinding, database security and generic list binding with XML security. We also had a glance over the BarItem class and how the design of this class is set up to be used by the RadOutlookBarItem and RadMenuItem classes. Resources for Article: Further resources on this subject: WPF 4.5 Application and Windows [Article] Windows Presentation Foundation Project - Basics of Working [Article] Introduction to Web Experience Factory [Article]
Read more
  • 0
  • 0
  • 1712

article-image-finishing-touches-and-publishing
Packt
18 Feb 2014
7 min read
Save for later

Finishing Touches and Publishing

Packt
18 Feb 2014
7 min read
(For more resources related to this topic, see here.) Publishing a Video Demo project Due to its very nature, a Video Demo project can only be published as an .mp4 video file. In the following exercise, you will return to the encoderVideo.cpvc project and explore the available publishing options: Open the encoderVideo.cpvc file under Chapter08. Make sure the file opens in Edit mode. If you are not in Edit mode, click on the Edit button at the lower-right corner of the screen. (If the Edit button is not displayed on the screen, it simply means that you already are in Edit mode.) When the file is open in Edit mode, take a look at the main toolbar at the top of the interface. Click on the Publish icon or navigate to File | Publish. The Publish Video Demo dialog opens. In the Publish Video Demo dialog, make sure the Name of the project is encoderVideo. Click on the … button and choose the publish folder of your exercises as the destination of the published video file. Open the Preset dropdown. Take some time to inspect the available presets. When done, choose the Video - Apple iPad preset. Make sure the Publish Video Demo dialog looks similar to what is shown in the following screenshot and click on the Publish button: Publishing a Video Demo project can be quite a lengthy process, so be patient. When the process is complete, a message asks you what to do next. Notice that one of the options enables you to upload your newly created video to YouTube directly. Click on the Close button to discard the message. Use Windows Explorer (Windows) or the Finder (Mac) to go to the publish folder of your exercises. Double-click on the encoderDemo.mp4 file to open the video in the default video player of your system. Remember that a Video Demo project can only be published as a video file. Also remember that the published .mp4 video file can only be experienced in a linear fashion and does not support any kind of interactivity. Publishing to Flash In the history of Captivate, publishing to Flash has always been the primary publishing option. Even though HTML5 publishing is a game changer, publishing to Flash is still an important capability of Captivate. Remember that this publishing format is currently the only one that supports every single feature, animation, and object of Captivate. In the following exercise, you will publish the Encoder Demonstration project in Flash using the default options: Return to the encoderDemo_800.cptx file under Chapter08. Click on the Publish icon situated right next to the Preview icon. Alternatively, you can also navigate to File | Publish. The Publish dialog box opens as shown in the following screenshot: Notice that the Publish dialog of a regular Captivate project contains far more options than its Publish Video Demo counterpart in .cpvc files. The Publish dialog box is divided into four main areas: The Publish Format area (1): This is where you choose the format in which you want to publish your projects. Basically, there are three options to choose from: SWF/HTML5, Media, and Print. The other options (E-Mail, FTP, and Adobe Connect) are actually suboptions of the SWF/HTML5, Media, and Print formats. The Output Format Options area (2): The content of this area depends on the format chosen in the Publish Format (1) area. The Project Information area (3): This area is a summary of the main project preferences and metadata. Clicking on the links of this area will bring you back to the corresponding preferences dialog boxes. The Advanced Options area (4): This area provides some additional advanced publishing options. You will now move on to the actual publication of the project in the Flash format. In the leftmost column of the Publish dialog, make sure the chosen format is SWF/HTML5. In the central area, change the Project Title to encoderDemo_800_flash. Click on the Browse… button situated just below the Folder field and choose to publish your movie in the publish folder of your exercises folder. Make sure the Publish to Folder checkbox is selected. Take a quick look at the remaining options, but leave them all at their current settings. Click on the Publish button at the bottom-right corner of the Publish dialog box. When Captivate has finished publishing the movie, an information box appears on the screen asking whether you want to view the output. Click on No to discard the information box and return to Captivate. You will now use the Finder (Mac) or the Windows Explorer (Windows) to take a look at the files Captivate has generated. Use the Finder (Mac) or the Windows Explorer (Windows) to browse to the publish folder of your exercises. Because you selected the Publish to Folder checkbox in the Publish dialog, Captivate has automatically created the encoderDemo_800_flash subfolder in the publish folder. Open the encoderDemo_800_flash subfolder to inspect its content.There should be five files stored in this location: encoderDemo_800_flash.swf: This is the main Flash file containing the compiled version of the .cptx project encoderDemo_800_flash.html: This file is an HTML page used to wrap the Flash file standard.js: This is a JavaScript file used to make the Flash player work well within the HTML page demo_en.flv: This is the video file used on slide 2 of the movie captivate.css: This file provides the necessary style rules to ensure there is proper formatting of the HTML page If you want to embed the compiled Captivate movie in an existing HTML page, only the .swf file (plus, in this case, the .flv video) is needed. The HTML editor (such as Adobe Dreamweaver) will recreate the necessary HTML, JavaScript, and CSS files. Captivate and DreamweaverAdobe Dreamweaver CC is the HTML editor of the Creative Cloud and the industry-leading solution for authoring professional web pages. Inserting a Captivate file in a Dreamweaver page is dead easy! First, move or copy the main Flash file (.swf) as well as the needed support files (in this case, the .flv video file), if any, somewhere in the root folder of the Dreamweaver site. When done, use the Files panel of Dreamweaver to drag and drop the main .swf file onto the HTML page. That's it! More information on Dreamweaver can be found at http://www.adobe.com/products/dreamweaver.html. You will now test the compiled project in a web browser. This is an important test as it closely recreates the conditions in which the students will experience the movie once uploaded on a web server. Double-click on the encoderDemo_800_flash.html file to open it in a web browser. Enjoy the final version of the demonstration you have created! Now that you have experienced the workflow of publishing the project to Flash with the default options, you will explore some additional publishing options. Using the Scalable HTML content option Thanks to Scalable HTML content option of Captivate, the eLearning content is automatically resized to fit the screen on which it is viewed. Let's experiment with this option hands on using the following steps: If needed, return to the encoderDemo_800.cptx file under Chapter08. Click on the Publish icon situated right next to the Preview icon. Alternatively, you can also navigate to File | Publish. In the leftmost column, make sure the chosen format is SWF/HTML5. In the central column, change the Project Title to encoderDemo_800_flashScalable. Click on the Browse… button situated just below the Folder field and ensure that the publish folder is still the publish folder of your exercises. Make sure the Publish to Folder checkbox is selected. In the Advanced Options section (lower-right corner of the Publish dialog), select the Scalable HTML content checkbox. Leave the remaining options at their current value and click on the Publish button at the bottom-right corner of the Publish dialog box. When Captivate has finished publishing the movie, an information box appears on the screen asking whether you want to view the output. Click on Yes to discard the information box and open the published movie in the default web browser. During the playback, use your mouse to resize your browser window and notice how the movie is resized and always fits the available space without being distorted. The Scalable HTML content option also works when the project is published in HTML5.
Read more
  • 0
  • 0
  • 11054

article-image-creating-attention-grabbing-pricing-tables
Packt
17 Feb 2014
6 min read
Save for later

Creating attention-grabbing pricing tables

Packt
17 Feb 2014
6 min read
(For more resources related to this topic, see here.) Let's revisit the mockup of how our client would like the pricing tables to look on desktop-sized screens: Let's see how close we can get to the desired result, and what we can work out for other viewport sizes. Setting up the variables, files, and markup As shown in the preceding screenshot, there are a few tables in this design. We can begin by adjusting a few fundamental variables for all tables. These are found in _variables.less. Search for the tables section and adjust the variables for background, accented rows, and borders as desired. I've made these adjustments as shown in the following lines of code: // Tables // ------------------------- ... @table-bg: transparent; // overall background-color @table-bg-accent: hsla(0,0,1%,.1); // for striping @table-bg-hover: hsla(0,0,1%,.2); @table-bg-active: @table-bg-hover; @table-border-color: #ccc; // table and cell border Save the file, compile it to CSS, and refresh to see the result as shown in the following screenshot: That's a start. Now we need to write the more specific styles. The _page-contents.less file is now growing long, and the task before us is extensive and highly focused on table styles. To carry the custom styles, let's create a new LESS file for these pricing tables: Create _pricing-tables.less in the main less folder. Import it into __main.less just after _page-contents.less as shown in the following line: @import "_pricing-tables.less"; Open _pricing-tables.less in your editor and begin writing your new styles. But before we begin writing styles, let's review the markup that we'll be working with. We have the following special classes already provided in the markup on the parent element of each respective table:   package package-basic package package-premium package package-pro   Thus, for the first table, you'll see the following markup on its parent div: <div class="package package-basic col-lg-4"> <table class="table table-striped"> ... Similarly, we'll use package package-premium and package package-pro for the second and third table, respectively. These parent containers obviously also provide basic layout instructions using the col-md-4 class to set up a three-column layout in medium viewports. Next, we will observe the markup for each table. We see that the basic table and table-striped classes have been applied: <table class="table table-striped"> The table uses the <thead> element for its top-most block. Within this, there is <th> spanning two columns, with an <h2> heading for the package name and <div class="price"> to markup the dollar amount: <thead><tr><th colspan="2"><h2>Basic Plan</h2><div class="price">$19</div></th></tr></thead> Next is the tfoot tag with the Sign up Now! button: <tfoot><tr><td colspan="2"><a href="#" class="btn">Sign upnow!</a></td></tr></tfoot> Then is the tbody tag with the list of features laid out in a straightforward manner in rows with two columns: <tbody><tr><td>Feature</td><td>Name</td></tr><tr><td>Feature</td><td>Name</td></tr><tr><td>Feature</td><td>Name</td></tr><tr><td>Feature</td><td>Name</td></tr><tr><td>Feature</td><td>Name</td></tr></tbody> And finally, of course, the closing tags for the table and parent div tags: </table></div><!-- /.package .package-basic --> Each table repeats this basic structure. This gives us what we need to start work! Beautifying the table head To beautify the thead element of all of our tables, we'll do the following: Align the text at the center Add a background color—for now, add a gray color that is approximately a midtone similar to the colors we'll apply to the final version Turn the font color white Convert the h2 heading to uppercase Increase the size of the price table Add the necessary padding all around the tables We can apply many of these touches with the following lines of code. We'll specify the #signup section as the context for these special table styles: #signup {table {border: 1px solid @table-border-color;thead th {text-align: center;background-color: @gray-light;color: #fff;padding-top: 12px;padding-bottom: 32px;h2 {text-transform: uppercase;}}}} In short, we've accomplished everything except increasing the size of the price tables. We can get started on this by adding the following lines of code, which are still nested within our #signup table selector: .price { font-size: 7em; line-height: 1;} This yields the following result: This is close to our desired result, but we need to decrease the size of the dollar sign. To give ourselves control over that character, let's go to the markup and wrap a span tag around it: <em class="price"><span>$</span>19</em> Remember to do the same for the other two tables. With this new bit of markup in place, we can nest this within our styles for .price: .price {...span {font-size: .5em;vertical-align: super;} These lines reduce the dollar sign to half its size and align it at the top. Now to recenter the result, we need to add a bit of negative margin to the parent .price selector: .price {margin-left: -0.25em;... The following screenshot shows the result: Styling the table body and foot By continuing to focus on the styles that apply to all three pricing tables, let's make the following adjustments: Add left and right padding to the list of features Stretch the button to full width Increase the button size We can accomplish this by adding the following rules: #signup {table {...tbody {td {padding-left: 16px;padding-right: 16px;}}a.btn {.btn-lg;display: block;width: 100%;background-color: @gray-light;color: #fff;}}} Save the file, compile it to CSS, and refresh the browser. You should see the following result: We're now ready to add styles to differentiate our three packages.
Read more
  • 0
  • 0
  • 12033
article-image-how-expand-your-knowledge
Packt
17 Feb 2014
11 min read
Save for later

How to Expand your Knowledge

Packt
17 Feb 2014
11 min read
(For more resources related to this topic, see here.) One of the most frequently asked questions on help forums probably is, "How can I learn Google Apps Script?" The answer is almost always the same: learn JavaScript and follow the numerous tutorials available on the Internet. No doubt, it is one of the possible ways to learn but it is also one of the most difficult ways. I shall express my opinion on that subject at the end of this article, but let us first summarize what we really need to be able to use Google Apps Script efficiently. The first and most important thing we must have is a clear idea of what we want to achieve. This seems a bit silly because we think, "Oh well, of course I know what I want; I just don't know how to do it!" As a matter of fact, this is often not the case. Let us have an example: a colleague asked me recently how he could count the time he was spending at school for meetings and other administrative tasks, not taking into account his hours as a teacher. This was supposed to be a simple problem as everyone in our school has a personal calendar in which all the events that we are invited to are recorded. So, he began to search for a way to collect every possible event from his calendar to a spreadsheet and from there—since he can definitely use a spreadsheet—he intended to do some data filtering to get the result he wanted. I told him to have a look at the Google Apps Script documentation and see what tools he had, to pick up data from calendars and import them into a spreadsheet. A few days later, he came back to me complaining that he didn't find any appropriate methods to do what he needs to. And, in a way, he was right; nowhere is such a workflow explained and it is actually not surprising. One can't imagine compiling all the possible workflows into a single help resource; there are definitely too many different use cases, each of them needing a particular approach. We had a discussion where I told him to think about his research as a series of simple and accurate parts and steps before trying to get the whole process in one stroke. The following is what he told me another few days later: "I knew nothing about this macro language, so I discovered that it is based on JavaScript with the addition of Google's own services that use a similar syntax and that the whole thing is composed of functions calling each other and having parameters. Then, I examined the calendar service and saw that it needs so-called date objects to choose a start and end date. Date object methods are pretty well explained on Mozilla's page, so once I got that I had an array of events, I thought what the heck is an array of objects? You gave me the link to this w3schools site, so I took a look at their definition; that was enough for me to go further and discover that I could use a loop to handle each event separately. Google documentation shows all the methods to get events details; that part was easy and now I have all my calendar events with dates, hours, description, title. All of it! I tell you." I'm not going to transcribe all of our conversation—it finally took a couple of hours—but towards the end, he was describing the process so well that the actual writing of his script was almost just a formality. With the help of the Content assist (autocomplete) feature of the script editor and a couple of browser tabs left open on JavaScript and Google documentation, he managed to write his script in one day. Of course, the script was not perfect and by no way optimized his speed or gave  nice-looking results, but it worked and he had the data he was looking for. At that point, he could post his script on a help forum if something went wrong or try to improve another version if he's a perfectionist, but that depends only on his will to go further or not. I would simply say one thing: you will learn what you need. If you don't need it, don't try to learn it as you will forget it faster than you learned it. If you do, then be prepared to need something else right after; it is an endless journey! JavaScript versus Google Apps Script The following is stated on the overview of Google Apps Script documentation page: Google Apps Script is a scripting language based on JavaScript that lets you do new and cool things with Google Apps like Docs, Sheets, and Forms. They should use a bigger typeface to make it more visible! The keyword here is based on JavaScript because it does indeed use most of the JavaScript Version 1.6 (with some portions of Version 1.7 and Version 1.8) and its general syntax. But, it has so many other methods that knowing only JavaScript is clearly not sufficient to use it adequately. I would even say that you can learn it step-by-step when you need it, looking for information on a specific item each time you use a new type of object. The following is the code that was used to get the integer part of the result that uses the getTime method: function myAgeInHours(){  var myBirthDate = new Date('1958/02/19 02:00:00').getTime();  myBirthDate = parseInt(myBirthDate/3600000, 10);  var today = parseInt(new Date().getTime()/3600000, 10);  return today-myBirthDate;} We looked at the documentation about the Date object to find the getTime() method and then found parseInt to get the integer part of the result. Well, I'm convinced that this approach is more efficient than spending hours on a site or in a book that shows all JavaScript information from A to Z. We have the opportunity to have powerful search engines in our browsers, so let's use them; they always find the answer for us in less time than it takes to write the question. Concerning methods specific to Google Apps Script, I think the approach should be different. The Google API documentation is pretty well organized and is full of code examples that clearly show us how to use almost every single method. If we start a project in a spreadsheet, it is a good idea to carefully read the section about spreadsheets (https://developers.google.com/apps-script/reference/spreadsheet) at least once and just check if what it says makes any sense. For example, in the Sheet class, I found this description: Returns the range with the top left cell at the given coordinates, and with the given number of rows. The following screenshot displays the same description: If I understand what range and co-ordinates are, then I probably know enough to be able to use that method (getRange(row, column, numRows) or a similar one. You want me to tell you the truth? I didn't know we could get a range this way by simply defining the top-left cell and just the number of rows (only three parameters). I always use the next one in the list, which is shown as follows: The description says: Returns the range with the top left cell at the given coordinates with the given number of rows and columns. So after all this time I spent on dozens of spreadsheet scripts, there still are methods that I can't even imagine exist! That's actually a nice confirmation of what I was suggesting: one doesn't need to know everything to be able to use it but it's always a good idea to read the docs from time to time. Infinite resources JavaScript is a very popular language; there are thousands of websites that show us examples and explain methods and functions. We must add all the forums and Q&A sites that return many results when we search something on Google to these websites (or any other search engine), and that is actually an unforeseen difficulty. It happens quite often that we find false information or code snippets that simply don't work, either because they have typos in them or they are so badly written that they work only in a very specific and peculiar situation. My personal solution is to use only a couple of websites and perform a search on their search engine, avoiding all the sources I'm not sure of. Maybe I miss something at times, but at least the information I get is trustworthy. Last but certainly not least, the help forum recommended by the Google Apps Script team, http://stackoverflow.com/questions/tagged/google-apps-script (with the google-apps-script tag), is certainly the best resource that is available. With more than 5000 questions (as of January, 2014), the help forum probably has threads about every possible use case and an important part of it has answers as well. There are of course other interesting tags: JavaScript, Google docs, Google spreadsheets, and a few even more specific ones. I have rarely seen really bad answers—although it does happen sometimes—simply because so many people read these posts that they generally flag or comment answers that show wrong information. There are also people from Google that regularly keep an eye on it and clarify any ambiguous response. Being a newbie is, by definition, temporary When I began to use Google spreadsheets and scripts, I found the Google Group Help forum (which does not exist anymore) an invaluable source of information and help, so I asked dozens of questions—some of them very basic and naive—and always got answers. After a while, since I was spending hours on this forum reading about every post I found, I began to answer too. I was so proud of being able to answer a question! It was almost like passing an examination; I knew that one of the experts there was going to read what I wrote and evaluate my knowledge; quite stressful but also satisfying when you don't fail! So after a couple of months I gained my first level point (on the Google Group forum, there are no reputation points but levels, starting from 1 for new arriving members up to TC(Top Contributors), whose level is unknown but is generally more than 15 or 20; anyway, that's not important). That little story is just a way to encourage any beginner to spend some time on this forum and consider every question as a challenge and try to answer it. Of course, there is no need to publish your answer every time as there are chances that you may get it all wrong, but just use this as an exercise that will give you more and more expertise. From time to time, you'll be able to be the first or best answerer and gain a few reputation points; consider it as a game, just a funny game where all you can finally win is knowledge and all you can lose is your newbie status—not a bad deal after all! Try to find your own best learning method I'm certainly not pretending that I know the best learning method for anyone. All the tips I presented in the previous section did work for me—and for a few other people I know—but there is no magic formula that would suit everyone. I know that each of us has a different background and follows a different path, but I wanted to say loud and clear that you don't need to have to be a graduate in IT to begin with Google Apps Script nor do you have to spend hours learning rules and conventions. Practice will make it easier everyday and motivation will give you enough energy to complete your projects, from simple ones to more ambiguous ones. Summary This article has given an overview of the many resources available to improve your learning experience. There are certainly more that I don't know of but as I already mentioned a few times before, we have powerful search engines in our browsers to help us. We also have to keep in mind that Google Apps Script will probably be different as compared to what it will be in a couple of years. Resources for Article: Further resources on this subject: Google Apps: Surfing the Web [Article] Developing apps with the Google Speech APIs [Article] Data Modeling and Scalability in Google App [Article]
Read more
  • 0
  • 0
  • 2496

article-image-creating-shipping-module
Packt
17 Feb 2014
12 min read
Save for later

Creating a Shipping Module

Packt
17 Feb 2014
12 min read
(For more resources related to this topic, see here.) Shipping ordered products to customers is one of the key parts of the e-commerce flow. In most cases, a shop owner has a contract with a shipping handler where everyone has their own business rules. In a standard Magento, the following shipping handlers are supported: DHL FedEx UPS USPS If your handler is not on the list, have a look if there is a module available at Magento Connect. If not, you can configure a standard shipping method or you can create your own, which we will do in this article. Initializing module configurations In this recipe, we will create the necessary files for a shipping module, which we will extend with more features using the recipes of this article. Getting ready Open your code editor with the Magento project. Also, get access to the backend where we will check some things. How to do it... The following steps describe how we can create the configuration for a shipping module: Create the following folders: app/code/local/Packt/ app/code/local/Packt/Shipme/ app/code/local/Packt/Shipme/etc/ app/code/local/Packt/Shipme/Model/ app/code/local/Packt/Shipme/Model/Carrier Create the module file named Packt_Shipme.xml in the folder app/etc/modules with the following content: <?xml version="1.0"?> <config> <modules> <Packt_Shipme> <active>true</active> <codePool>local</codePool> <depends> <Mage_Shipping /> </depends> </Packt_Shipme> </modules> </config> Create a config.xml file in the folder app/code/local/Packt/Shipme/etc/ with the following content: <?xml version="1.0" encoding="UTF-8"?> <config> <modules> <Packt_Shipme> <version>0.0.1</version> </Packt_Shipme> </modules> <global> <models> <shipme> <class>Packt_Shipme_Model</class> </shipme> </models> </global> <default> <carriers> <shipme> <active>1</active> <model>shipme/carrier_shipme</model> <title>Shipme shipping</title> <express_enabled>1</express_enabled> <express_title>Express delivery</express_title> <express_price>4</express_price> <business_enabled>1</business_enabled> <business_title>Business delivery</business_title> <business_price>5</business_price> </shipme> </carriers> </default> </config> Clear the cache and navigate in the backend to System | Configuration | Advanced Disable Modules Output. Observe that the Packt_Shipme module is on the list. At this point, the module is initialized and working. Now, we have to create a system.xml file where we will put the configuration parameters for our shipping module. Create the file app/code/local/Packt/Shipme/etc/system.xml. In this file, we will create the configuration parameters for our shipping module. When you paste the following code in the file, you will create an extra group in the shipping method's configuration. In this group, we can set the settings for the new shipping method: <?xml version="1.0" encoding="UTF-8"?> <config> <sections> <carriers> <groups> <shipme translate="label" module="shipping"> <label>Shipme</label> <sort_order>15</sort_order> <show_in_default>1</show_in_default> <show_in_website>1</show_in_website> <show_in_store>1</show_in_store> <fields> <!-- Define configuration fields below --> <active translate="label"> <label>Enabled</label> <frontend_type>select</frontend_type> <source_model>adminhtml/ system_config_source_yesno</source_model> <sort_order>10</sort_order> <show_in_default>1</show_in_default> <show_in_website>1</show_in_website> <show_in_store>1</show_in_store> </active> <title translate="label"> <label>Title</label> <frontend_type>text</frontend_type> <sort_order>20</sort_order> <show_in_default>1</show_in_default> <show_in_website>1</show_in_website> <show_in_store>1</show_in_store> </title> </fields> </shipme> </groups> </carriers> </sections> </config> Clear the cache and navigate in the backend to the shipping method configuration page. To do that, navigate to System | Configuration | Sales | Shipping methods. You will see that an extra group is added as shown in the following screenshot: You will see that there is a new shipping method called Shipme. We will extend this configuration with some values. Add the following code under the <fields> tag of the module: <active translate="label"> <label>Enabled</label> <frontend_type>select</frontend_type> <source_model>adminhtml/system_config_source_yesno</source_ model> <sort_order>10</sort_order> <show_in_default>1</show_in_default> <show_in_website>1</show_in_website> <show_in_store>1</show_in_store> </active> <title translate="label"> <label>Title</label> <frontend_type>text</frontend_type> <sort_order>20</sort_order> <show_in_default>1</show_in_default> <show_in_website>1</show_in_website> <show_in_store>1</show_in_store> </title> <express_enabled translate="label"> <label>Enable express</label> <frontend_type>select</frontend_type> <source_model>adminhtml/system_config_source_yesno</source_ model> <sort_order>30</sort_order> <show_in_default>1</show_in_default> <show_in_website>1</show_in_website> <show_in_store>1</show_in_store> </express_enabled> <express_title translate="label"> <label>Title express</label> <frontend_type>text</frontend_type> <sort_order>40</sort_order> <show_in_default>1</show_in_default> <show_in_website>1</show_in_website> <show_in_store>1</show_in_store> </express_title> <express_price translate="label"> <label>Price express</label> <frontend_type>text</frontend_type> <sort_order>50</sort_order> <show_in_default>1</show_in_default> <show_in_website>1</show_in_website> <show_in_store>1</show_in_store> </express_price> <business_enabled translate="label"> <label>Enable business</label> <frontend_type>select</frontend_type> <source_model>adminhtml/system_config_source_yesno</source_ model> <sort_order>60</sort_order> <show_in_default>1</show_in_default> <show_in_website>1</show_in_website> <show_in_store>1</show_in_store> </business_enabled> <business_title translate="label"> <label>Title business</label> <frontend_type>text</frontend_type> <sort_order>70</sort_order> <show_in_default>1</show_in_default> <show_in_website>1</show_in_website> <show_in_store>1</show_in_store> </business_title> <business_price translate="label"> <label>Price business</label> <frontend_type>text</frontend_type> <sort_order>80</sort_order> <show_in_default>1</show_in_default> <show_in_website>1</show_in_website> <show_in_store>1</show_in_store> </business_price> Clear the cache and reload the backend. You will now see the other configurations under the Shipme – Express shipping method as shown in the following screenshot: How it works... The first thing we have done is to create the necessary files to initialize the module. The following files are required to initialize a module: app/etc/modules/Packt_Shipme.xml app/code/local/Packt/Shipme/etc/config.xml In the first file, we will activate the module with the <active> tag. The <codePool> tag describes that the module is located in the local code pool, which represents the folder app/code/local/. In this file, there is also the <depends> tag. First this will check if the Mage_Shipping module is installed or not. If not, Magento will throw an exception. If the module is available, the dependency will load this module after the Mage_Shipping module. This makes it possible to rewrite some values from the Mage_Shipping module. In the second file, config.xml, we configured all the stuff that we will need in this module. These are the following things: The version number (0.0.1) The models Some default values for the configuration values The last thing we did was create a system.xml file so that we can create a custom configuration for the shipping module. The configuration in the system.xml file adds some extra values to the shipping method configuration, which is available in the backend under the menu System | Configuration | Sales | Shipping methods. In this module, we created a new shipping handler called Shipme. Within this handler, you can configure two shipping options: express and business. In the system.xml file, we created the fields to configure the visibility, name, and price of the options. See also In this recipe, we used the system.xml file of the module to create the configuration values. Writing an adapter model A new shipping module is initialized in the previous recipe. What we did in the previous recipe was a preparation to continue with the business part we will see in this recipe. We will add a model with the business logic for the shipping method. The model is called an adapter class because Magento requires an adapter class for each shipping method. This class will extend the Mage_Shipping_Model_Carrier_Abstract class. This class will be used for the following things: Make the shipping method available Calculate the shipping costs Set the title in the frontend of the shipping methods How to do it... Perform the following steps to create the adapter class for the shipping method: Create the folder app/code/local/Packt/Shipme/Model/Carrier if it doesn't already exist. In this folder, create a file named Shipme.php and add the following content to it: <?php class Packt_Shipme_Model_Carrier_Shipme extends Mage_Shipping_Model_Carrier_Abstract implements Mage_Shipping_Model_Carrier_Interface { protected $_code = 'shipme'; public function collectRates (Mage_Shipping_Model_Rate_Request $request) { $result = Mage::getModel('shipping/rate_result'); //Check if express method is enabled if ($this->getConfigData('express_enabled')) { $method = Mage::getModel ('shipping/rate_result_method'); $method->setCarrier($this->_code); $method->setCarrierTitle ($this->getConfigData('title')); $method->setMethod('express'); $method->setMethodTitle ($this->getConfigData('express_title')); $method->setCost ($this->getConfigData('express_price')); $method->setPrice ($this->getConfigData('express_price')); $result->append($method); } //Check if business method is enabled if ($this->getConfigData('business_enabled')) { $method = Mage::getModel ('shipping/rate_result_method'); $method->setCarrier($this->_code); $method->setCarrierTitle ($this->getConfigData('title')); $method->setMethod('business'); $method->setMethodTitle ($this->getConfigData('business_title')); $method->setCost ($this->getConfigData('business_price')); $method->setPrice ($this->getConfigData('business_price')); $result->append($method); } return $result; } public function isActive() { $active = $this->getConfigData('active'); return $active==1 || $active=='true'; } public function getAllowedMethods() { return array('shipme'=>$this->getConfigData('name')); } } Save the file and clear the cache; your adapter model has now created. How it works... The previously created class handles all the business logic that is needed for the shipping method. Because this adapter class is an extension of the Mage_Shipping_Model_Carrier_Abstract class, we can overwrite some methods to customize the business logic of the standard. The first method we overwrite is the isAvailable() function. In this function, we have to return true or false to say that the module is active. In our code, we will activate the module based on the system configuration field active. The second method is the collectRates() function. This function is used to set the right parameters for every shipping method. For every shipping method, we can set the title and price. The class implements the interface Mage_Shipping_Model_Carrier_Interface. In this interface, two functions are declared: the isTrackingAvailable() and getAllowedMethods() functions. We created the function getAllowedMethods() in the adapter class. The isTrackingAvailable() function is declared in the parent class Mage_Shipping_Model_Carrier_Abstract. We configured two options under the Shipme shipping method. These options are called Express delivery and Business delivery. We will check if they are enabled in the configuration and set the configured title and price for each option. The last thing to do is return the right values. We have to return an instance of the class Mage_Shipping_Model_Rate_Result. We created an empty instance of the class, where we will append the methods to when they are available. To add a method, we have to use the function append($method). This function requires an instance of the class Mage_Shipping_Model_Rate_Result_Method that we created in the two if statements.
Read more
  • 0
  • 0
  • 11871

Packt
14 Feb 2014
6 min read
Save for later

CreateJS – Performing Animation and Transforming Function

Packt
14 Feb 2014
6 min read
(For more resources related to this topic, see here.) Creating animations with CreateJS As you may already know, creating animations in web browsers during web development is a difficult job because you have to write code that has to work in all browsers; this is called browser compatibility. The good news is that CreateJS provides modules to write and develop animations in web browsers without thinking about browser compatibility. CreateJS modules can do this job very well and all you need to do is work with CreateJS API. Understanding TweenJS TweenJS is one of the modules of CreateJS that helps you develop animations in web browsers. We will now introduce TweenJS. The TweenJS JavaScript library provides a simple but powerful tweening interface. It supports tweening of both numeric object properties and CSS style properties, and allows you to chain tweens and actions together to create complex sequences.—TweenJS API Documentation What is tweening? Let us understand precisely what tweening means: Inbetweening or tweening is the process of generating intermediate frames between two images to give the appearance that the first image evolves smoothly into the second image.—Wikipedia The same as other CreateJS subsets, TweenJS contains many functions and methods; however, we are going to work with and create examples for specific basic methods, based on which you can read the rest of the documentation of TweenJS to create more complex animations. Understanding API and methods of TweenJS In order to create animations in TweenJS, you don't have to work with a lot of methods. There are a few functions that help you to create animations. Following are all the methods with a brief description: get: It returns a new tween instance. to: It queues a tween from the current values to the target properties. set: It queues an action to set the specified properties on the specified target. wait: It queues a wait (essentially an empty tween). call: It queues an action to call the specified function. play: It queues an action to play (un-pause) the specified tween. pause: It queues an action to pause the specified tween. The following is an example of using the Tweening API: var tween = createjs.Tween.get(myTarget).to({x:300},400). set({label:"hello!"}).wait(500).to({alpha:0,visible:false},1000). call(onComplete); The previous example will create a tween, which: Tweens the target to an x value of 300 with duration 400ms and sets its label to hello!. Waits 500ms. Tweens the target's alpha property to 0with duration 1s and sets the visible property to false. Finally, calls the onComplete function. Creating a simple animation Now, it's time to create our simplest animation with TweenJS. It is a simple but powerful API, which gives you the ability to develop animations with method chaining. Scenario The animation has a red ball that comes from the top of the Canvas element and then drops down. In the preceding screenshot, you can see all the steps of our simple animation; consequently, you can predict what we need to do to prepare this animation. In our animation,we are going to use two methods: get and to. The following is the complete source code for our animation: var canvas = document.getElementById("canvas"); var stage = new createjs.Stage(canvas); var ball = new createjs.Shape(); ball.graphics.beginFill("#FF0000").drawCircle(0, 0, 50); ball.x = 200; ball.y = -50; var tween = createjs.Tween.get(ball) to({ y: 300 }, 1500, createjs.Ease.bounceOut); stage.addChild(ball); createjs.Ticker.addEventListener("tick", stage); In the second and third line of the JavaScript code snippet, two variables are declared, namely; the canvas and stage objects. In the next line, the ball variable is declared, which contains our shape object. In the following line, we drew a red circle with the drawCircle method. Then, in order to set the coordinates of our shape object outside the viewport, we set the x axis to -50 px. After this, we created a tween variable, which holds the Tween object; then, using the TweenJS method chaining, the to method is called with duration of 1500 ms and the y property set to 300 px. The third parameter of the to method represents the ease function of tween, which we set to bounceOut in this example. In the following lines, the ball variable is added to Stage and the tick event is added to the Ticker class to keep Stage updated while the animation is playing. You can also find the Canvas element in line 30, using which all animations and shapes are rendered in this element. Transforming shapes CreateJS provides some functions to transform shapes easily on Stage. Each DisplayObject has a setTransform method that allows the transforming of a DisplayObject (like a circle). The following shortcut method is used to quickly set the transform properties on the display object. All its parameters are optional. Omitted parameters will have the default value set. setTransform([x=0] [y=0] [scaleX=1] [scaleY=1] [rotation=0] [skewX=0] [skewY=0] [regX=0] [regY=0]) Furthermore, you can change all the properties via DisplayObject directly (like scaleY and scaleX) as shown in the following example: displayObject.setTransform(100, 100, 2, 2); An example of Transforming function As an instance of using the shape transforming feature with CreateJS, we are going to extend our previous example: var angle = 0; window.ball; var canvas = document.getElementById("canvas"); var stage = new createjs.Stage(canvas); ball = new createjs.Shape(); ball.graphics.beginFill("#FF0000").drawCircle(0, 0, 50); ball.x = 200; ball.y = 300; stage.addChild(ball); function tick(event) { angle += 0.025; var scale = Math.cos(angle); ball.setTransform(ball.x, ball.y, scale, scale); stage.update(event); } createjs.Ticker.addEventListener("tick", tick); In this example, we have a red circle, similar to the previous example of tweening. We set the coordinates of the circle to 200 and 300 and added the circle to the stage object. In the next line, we have a tick function that transforms the shape of the circle. Inside this function, we have an angle variable that increases with each call. We then set the ballX and ballY coordinate to the cosine of the angle variable. The transforming done is similar to the following screenshot: This is a basic example of transforming shapes in CreateJS, but obviously, you can develop and create better transforming by playing with a shape's properties and values. Summary In this article, we covered how to use animation and transform objects on the page using CreateJS. Resources for Article: Further resources on this subject: Introducing a feature of IntroJs [Article] So, what is Node.js? [Article] So, what is Ext JS? [Article]
Read more
  • 0
  • 0
  • 5275
article-image-adding-health-checks
Packt
14 Feb 2014
3 min read
Save for later

Adding health checks

Packt
14 Feb 2014
3 min read
(For more resources related to this topic, see here.) A health check is a runtime test for our application. We are going to create a health check that tests the creation of new contacts using the Jersey client. The health check results are accessible through the admin port of our application, which by default is 8081. How to do it… To add a health check perform the following steps: Create a new package called com.dwbook.phonebook.health and a class named NewContactHealthCheck in it: import javax.ws.rs.core.MediaType; import com.codahale.metrics.health.HealthCheck; import com.dwbook.phonebook.representations.Contact; import com.sun.jersey.api.client.*; public class NewContactHealthCheck extends HealthCheck { private final Client client; public NewContactHealthCheck(Client client) { super(); this.client = client; } @Override protected Result check() throws Exception { WebResource contactResource = client .resource("http://localhost:8080/contact"); ClientResponse response = contactResource.type( MediaType.APPLICATION_JSON).post( ClientResponse.class, new Contact(0, "Health Check First Name", "Health Check Last Name", "00000000")); if (response.getStatus() == 201) { return Result.healthy(); } else { return Result.unhealthy("New Contact cannot be created!"); } } } Register the health check with the Dropwizard environment by using the HealthCheckRegistry#register() method within the #run() method of the App class. You will first need to import com.dwbook.phonebook.health.NewContactHealthCheck. The HealthCheckRegistry can be accessed using the Environment#healthChecks() method: // Add health checks e.healthChecks().register ("New Contact health check", new NewContactHealthCheck(client)); After building and starting your application, navigate with your browser to http://localhost:8081/healthcheck: The results of the defined health checks are presented in the JSON format. In case the custom health check we just created or any other health check fails, it will be flagged as "healthy": false, letting you know that your application faces runtime problems. How it works… We used exactly the same code used by our client class in order to create a health check; that is, a runtime test that confirms that the new contacts can be created by performing HTTP POST requests to the appropriate endpoint of the ContactResource class. This health check gives us the required confidence that our web service is functional. All we need for the creation of a health check is a class that extends HealthCheck and implements the #check() method. In the class's constructor, we call the parent class's constructor specifying the name of our check—the one that will be used to identify our health check. In the #check() method, we literally implement a check. We check that everything is as it should be. If so, we return Result.healthy(), else we return Result.unhealthy(), indicating that something is going wrong. Summary This article showed what a health check is and demonstrated how to add a health check. The health check we created tested the creation of new contacts using the Jersey client. Resources for Article: Further resources on this subject: RESTful Web Services – Server-Sent Events (SSE) [Article] Connecting to a web service (Should know) [Article] Web Services and Forms [Article]
Read more
  • 0
  • 0
  • 4467

article-image-using-phalcon-models-views-and-controllers
Packt
20 Jan 2014
3 min read
Save for later

Using Phalcon Models, Views, and Controllers

Packt
20 Jan 2014
3 min read
(For more resources related to this topic, see here.) Creating CRUD scaffolding CRUD stands for create, read, update, and delete, which are the four basic functions our application should do with our blog post records. Phalcon web tools will also help us to get these built. Click on the Scaffold tab on the web tools page and you will see a page as shown in the following screenshot: Select posts from the Table name list and volt from the Template engine list, and check Force this time, because we are going to force our new files to overwrite the old model and controller files that we just generated. Click on the Generate button and some magic should happen. Browse to http://localhost/phalconBlog/posts and you will see a page like the following screenshot: We finally have some functionality we can use. We have no posts, but we can create some. Click on the Create posts link and you will see a page similar to the one we were just at. The form will look nearly the same, but it will have a Create posts heading. Fill out the Title, Body, and Excerpt fields and click on the Save button. The form will post, and you will get a message stating that the post was created successfully. This will take you back to the post's index page. Now you should be able to search for and find the post you just created. If you forgot what you posted, you can click on Search without entering anything in the fields, and you should see a page like the following screenshot: This is not a very pretty or user-friendly blog application. But it got us started, and that's all we need. The next time we start a Phalcon project, it should only take a few minutes to go through these steps. Now we will look over our generated code, and as we do, modify it to make it more blog-like. Summary In this article, we worked on the model, view, and controller for the posts in our blog. To do this, we used Phalcon web tools to generate our CRUD scaffolding for us. Then, we modified this generated code so it would do what we need it to do. We can now add posts. We also learned about the Volt template engine. Resources for Article: Further resources on this subject: Using An Object Oriented Approach for Implementing PHP Classes to Interact with Oracle [Article] FuelPHP [Article] Installing PHP-Nuke [Article]
Read more
  • 0
  • 0
  • 2996
Modal Close icon
Modal Close icon