Posts Tagged 'Development'

March 26, 2013

Should My Startup Join an Accelerator/Incubator Program?

As part of my role at SoftLayer, I have the opportunity and privilege to mentor numerous entrepreneurs and startup teams when they partner with us through our Catalyst program. One question I hear often is, "Should I join an accelerator?" My answer: "That all depends." Let's look at the five lessons entrepreneurs should learn before they decide to join a startup accelerator or incubator program.

Lesson 1: The founders must be committed to the success of their venture.
Joining an accelerator or incubator comes with some strings attached — startups give up between 6 to 10 percent of their equity in exchange for some cash and structured program that usually lasts around three months. Obviously, this kind of commitment should not be taken lightly.

Too often, startups join accelerator programs before they are ready or mature enough as a team. Sometimes, a company's idea isn't fully baked, so they end up spending as much time "creating" their business as they do "accelerating" it. As a result, that company isn't able to leverage an accelerator's resources efficiently throughout the entire program ... The founders need to establish a vision for the business, begin laying the groundwork for the company's products and services, and be 100% committed to the accelerator program before joining. If you can't say with confidence that your startup meets all three of those requirements, don't do it. Take care of those three points and proceed to the next lesson.

Lesson 2: Be prepared to leverage what you are given.
Many startups join accelerator and incubator programs with unrealistic expectations. Participation in these programs — even the most exclusive and well-known ones — by no means guarantees that you'll raise additional money or have a successful exit. These programs provide startups with office space, free cloud services, and access to mentors, investors, recruiters and media ... Those outstanding services provide participating startups with a distinct competitive advantage, but they don't serve up success on a silver platter. If you aren't ready work tirelessly to leverage the benefits of a startup program, don't bother.

Lesson 3: Take advice and criticism well; mentors are trying to help.
"Mentorship" is very tough to qualify, and criticism is difficult to take ... Especially if you're 100% committed to your business and you don't want to be told that you've done something wrong. Mentors in these startup programs have "been there and done that," and they wouldn't be in a mentorship position if they weren't looking out for your best interest and the ultimate success of your company.

Look programs that take mentorship seriously and can provide a broad range of expertise from strategy to marketing and business development to software architecture to building and scaling IT infrastructure. Then be intentional about listening to the people around you.

Lesson 4: Do your research and make an informed decision.
With the proliferation of startups globally, we're also seeing an evolution in the accelerator ecosystem. There are a number accelerators being positioned to help support founders with ideas on a global, regional and local basis, but it's important to evaluate a program's vision with its execution of that vision. Not all startup programs are created equal, and some might not offer the right set of resources and opportunities for your team. When you're giving up equity in your company, you should have complete confidence that the accelerator or incubator you join will deliver on its side of the deal.

Lesson 5: Leverage the network and community you will meet.
When you've done your homework, applied and been accepted to the perfect startup program, meet everyone you can and learn from them. One of the most tangible benefits of joining an accelerator is the way you can fast track a business idea while boosting network contacts. Much in the way someone chooses a prestigious college or joins a fraternity, some of the most valuable resources you'll come across in these programs are the people you meet. In this way, accelerators and incubators are becoming a proxy for undergrad and graduate school ... The appeal for promising entrepreneurs is simple: Why wait to make a dent in the universe? Today, more people are going to college and fewer are landing well-paying jobs after graduation, so some of the world's best and brightest are turning to these communities and foregoing the more structured "higher education" process.

Even if your startup is plugging along smoothly, a startup accelerator or incubator program might be worth a look. Venture capitalists often trust programs like TechStars and 500 Startups to filter or vet early stage companies. If your business has the stamp of approval from one of these organizations, it's decidedly less risky than a business idea pitched by a random entrepreneur.

If you understand each of these lessons and you take advantage of the resources and opportunities provided by startup accelerators and incubators, the sky is the limit for your business. Now get to work.

Class dismissed.


March 19, 2013

iptables Tips and Tricks: CSF Configuration

In our last "iptables Tips and Tricks" installment, we talked about Advanced Policy Firewall (APF) configuration, so it should come as no surprise that in this installment, we're turning our attention to ConfigServer Security & Firewall (CSF). Before we get started, you should probably run through the list of warnings I include at the top of the APF blog post and make sure you have your Band-Aid ready in case you need it.

To get the ball rolling, we need to download CSF and install it on our server. In this post, we're working with a CentOS 6.0 32-bit server, so our (root) terminal commands would look like this to download and install CSF:

$ wget #Download CSF using wget.
$ tar zxvf csf.tgz #Unpack it.
$ yum install perl-libwww-perl #Make sure perl modules are installed ...
$ yum install perl-Time-HiRes  #Otherwise it will generate an error.
$ cd csf
$ ./ #Install CSF.
$ /etc/init.d/csf start #Start CSF. (Note: You can also use '$ service csf start')

Once you start CSF, you can see a list of the default rules that load at startup. CSF defaults to a DROP policy:

$ iptables -nL | grep policy
Chain INPUT (policy DROP)
Chain FORWARD (policy DROP)
Chain OUTPUT (policy DROP)

Don't ever run "iptables -F" unless you want to lock yourself out. In fact, you might want to add "This server is running CSF - do not run 'iptables -F'" to your /etc/motd, just as a reminder/warning to others.

CSF loads on startup by default. This means that if you get locked out, a simple reboot probably won't fix the problem. Runlevels 2, 3, 4, and 5 are all on:

$ chkconfig --list | grep csf
csf             0:off   1:off   2:on    3:on    4:on    5:on    6:off

Some features of CSF will not work unless you have certain iptables modules installed. I believe they are installed by default in CentOS, but if you custom-built your iptables, they might not all be installed. Run this script to see if all modules are installed:

$ /etc/csf/
Testing ip_tables/iptable_filter...OK
Testing ipt_LOG...OK
Testing ipt_multiport/xt_multiport...OK
Testing ipt_REJECT...OK
Testing ipt_state/xt_state...OK
Testing ipt_limit/xt_limit...OK
Testing ipt_recent...OK
Testing xt_connlimit...OK
Testing ipt_owner/xt_owner...OK
Testing iptable_nat/ipt_REDIRECT...OK
Testing iptable_nat/ipt_DNAT...OK
RESULT: csf should function on this server

As I mentioned, this is the default iptables installation on a minimal CentOS 6.0 image, so chances are good that these modules are already installed on your system. It never hurts to check, though.

The CSF Configuration File

The primary CSF configuration is stored in the well-documented /etc/csf/csf.conf file. CSF is extremely configurable, so there are a lot of options to read over. Let's take a look over some of the more important features:



This TESTING cron job runs every "5" minutes so you don't lock yourself out when you're testing your rules. When you are satisfied with your rules (and confident that you won't lock yourself out), you can set TESTING to "0".

Globally Allowed Ports

# Allow incoming TCP ports
TCP_IN = "20,21,22,25,53,80,110,143,443,465,587,993,995"
# Allow outgoing TCP ports
TCP_OUT = "20,21,22,25,53,80,110,113,443"
# Allow incoming UDP ports
UDP_IN = "20,21,53"
# Allow outgoing UDP ports
# To allow outgoing traceroute add 33434:33523 to this list
UDP_OUT = "20,21,53,113,123"

Incoming Ping Requests

# Allow incoming PING
ICMP_IN = "1"

Allowing ping is usually a good option for diagnostic purposes, so I don't recommend turning it off. Disallowing ping is an example of "security through obscurity," and it will not typically dissuade your attackers.

Ethernet Device


Here, you can configure iptables to ONLY use one Ethernet adapter. You might want to only guard your public network adapter in some situations.

IP Limit in Permanent "Deny" File


A higher number here will obviously screen out more IP addresses in csf.deny, but higher numbers also may cause slowdowns.

IP Limit in Temporary "Deny" File


Similar to DENY_IP_LIMIT, the DENY_TEMP_IP_LIMIT represents the maximum number of IPs that can be stored in the temporary ban list.

SMTP Blocking


When set to "1", SMTP_BLOCK does not completely block outbound SMTP, but it does block it for most users. This will prevent malicious scripts and compromised users from making outbound connections from unauthorized mail clients on the server. SMTP_BLOCK doesn't stop those scripts from running, but it does stop them from functioning. Mail sent through the proper channels will still be delivered normally.

Allowing SMTP on localhost


Custom Mail Port Designation

SMTP_PORTS = "25,465,587"

Allowing SMTP Access to Users/Groups

SMTP_ALLOWGROUP = "mail,mailman"

SYN Flood Protection


Per the documentation, you should only enable SYN flood protection (SYNFLOOD= "1") if you are currently under a SYN flood attack.

Concurrent Connections Limit

CONNLIMIT = "22;5,80;20"
PORTFLOOD = "22;tcp;5;300,80;tcp;20;5

These options allow you to add customized DoS protection. CONNLIMIT handles the number of concurrent connections, and in this example, we're limiting port 22 to 5 connections and port 80 to 20 connections.

PORTFLOOD watches the number of connections per a given number of seconds. In this example, we're limiting the TCP connection on port 22 to 5 connections/second with a quiet period of 300 seconds before the connection is unblocked. Additonally, we're limiting the TCP connection on port 80 to 20 connections/second with a quiet period of 5 seconds before the connection is unblocked.

Check the readme.txt file for more information about the syntax.

Logging to Syslog

SYSLOG = "0"

When enabled, this option logs lfd (Login Failure Daemon) messages to syslog as well as to /var/log/lfd.log.

Dropping v. Rejecting Packets


This configuration allows you to either DROP or REJECT packets. REJECT tells the sender that the packet has been blocked by the firewall. DROP just drops the packet and does not send a response. I like DROP better for regular use, but REJECT might be more helpful if you need to diagnose a connectivity issue.

Logging Dropped Connections


This option logs dropped connections to syslog. I don't see any reason to turn this off unless your hard drive is getting full.

Port Exceptions When Logging Dropped Connections

DROP_NOLOG = "67,68,111,113,135:139,445,500,513,520"

These ports are specifically blocked from being logged either to conserve hard drive space or make the log file easier to read.

"Watch Mode"


If you are ever stuck trying to troubleshoot a large ruleset, you might consider turning this option on. You can use it to track the actions to watched IP addresses to see where they are getting blocked or accepted.

Login Failure Daemon Alert


You can specify an email address to report errors from the Login Failure Daemon, which tracks and automatically blocks brute force login attempts.

Permanent Blocks and NetBlocks


These settings control the permanent block and netblock blocking. You probably don't need to touch these settings, but you might want some additional security or less security depending on your company needs. If something gets permablocked, it will require your intervention to clear it, which might create downtime for your clients. Likewise, if a legitimate IP address happens to be part of a netblock which has an attacking IP address on it, it will get blocked if you have that feature turned on. A class C network encompasses 256 IP addresses. You can set this to class B or A, but that could block thousands or millions of IP addresses, respectively. Unless you find yourself under constant attack, I would advise you to leave that LF_NETBLOCK off.

Additional Protection During Updates

# Safe Chain Update. If enabled, all dynamic update chains (GALLOW*, GDENY*,
# chain when updating, and insert it into the relevant LOCALINPUT/LOCALOUTPUT
# chain, then flush and delete the old dynamic chain and rename the new chain.
# This prevents a small window of opportunity opening when an update occurs and
# the dynamic chain is flushed for the new rules.

Activating this option will increase your system resource usage and will require more rules to be running at one time, but it provides an additional layer of protection during updates. Without this option turned on, your rules will be flushed for a short amount of time, leaving your server vulnerable.

Multi-Server Deployment Options


Like APF, you can configure global lists for multiple server deployments. You'll need to specify a URL of the text file with the IP addresses for the global lists.



This option enables the SPAMHAUS blocklist. Specify the number of seconds between refreshes. Recommended setting is 86400 (1 day).

Blocking TOR Exit IP Addresses

LF_TOR = "0"

Enabling this option will block TOR exit IP addresses. If you are not familiar with TOR, it is a completely anonymous proxy network. This could block some legitimate users who are trying to protect their anonymity, so I would recommend only turning this on if you are already under attack from a TOR exit address.

Blocking Bogon Addresses

LF_BOGON = "0"

Blocking bogon addresses (addresses that should not be possible) is usually a good decision. To enable, set the number of seconds between refreshes. I recommend enabling this option and setting the refresh at 86400 (1 day). If you do so, be sure to add your private network adapters to the skip list.

Country-Specific Access to Your Server

CC_DENY = ""

With these options, you can block or allow entire countries from accessing your server. To do so, enter the country codes in a comma separated list. Even though this generates a lot of additional rules, it's valuable to some sysadmins.


Alternatively, you can set your server to exclusively accept traffic from a list of country codes. All other countries not listed will have their traffic dropped. There are many other settings related to these options that I don't have time to cover in this blog.

Blocking Login Failures


This enables blocking of login failures (per service). There are a lot of great customization options in this section.

Scanning Directories for Malicious Files


This feature scans /tmp and /dev/shm for potentially malicious files and alerts you to their presence based on the interval you designate. You can also have CSF automatically quarantine malicious files with this option:


Distributed Attack Protection


By enabling this option, you activate additional protection against distributed attacks.

Blocking Based on Abusive Email Usage

LT_POP3D = "0"
LT_IMAPD = "0"

If a user checks email too many times per hour (more than the non-zero value specified), the user's IP address is blocked.

Email Alert Following Block


This will send you email when something is blocked. I'd recommend leaving it on.

Blocking IP Addresses Based on Number of Connections

CT_LIMIT = "0"

This feature tracks connections and blocks the IP if the number of connections is too high. Use caution because if you enable this option and set this value too low, it will block legitimate traffic.

Application-Level Protection

PT_LIMIT = "60"

This feature provides application level protection against malicious scripts that take a long time to execute.

Blocking Port Scanners

PS_LIMIT = "10"

Enabling HTML User Interface for CSF

UI = "0"

CSF has a built-in HTML user interface. You can enable this by setting UI = "1". There are a list of prerequisites for this option in the readme.txt.

Notifying Blocked IP Addresses


This option will notify blocked IP addresses when they have been blocked by the firewall.

Port Knocking


CSF supports port knocking, which is a technique that provides an additional layer of security. See for details.

Allow and Deny Lists

As we walked through the CSF configuration file, you saw that I referenced the csf.deny file, so it should come as no surprise that CSF also includes csf.allow to customize "allow" rules as well. If you are familiar with APF, these files have a very similar syntax ... Each entry is made up of the same four components: protocol|flow|port|IP. The only real difference being that APF uses the colon as a delimiter while CSF uses the pipe:

#APF Version
#CSF Version

Fortunately, replacing your colon with a pipe is a minimally invasive procedure that can be automated with a tool like vi.

CSF Command Line Tool

The command line tool for CSF is much more robust than the one for APF:

$ csf --help
csf: v5.79 (cPanel)
ConfigServer Security & Firewall
(c)2006-2013, Way to the Web Limited (
Usage: /usr/sbin/csf [option] [value]
Option              Meaning
-h, --help          Show this message
-l, --status        List/Show iptables configuration
-l6, --status6      List/Show ip6tables configuration
-s, --start         Start firewall rules
-f, --stop          Flush/Stop firewall rules (Note: lfd may restart csf)
-r, --restart       Restart firewall rules
-q, --startq        Quick restart (csf restarted by lfd)
-sf, --startf       Force CLI restart regardless of LF_QUICKSTART setting
-a, --add ip        Allow an IP and add to /etc/csf.allow
-ar, --addrm ip     Remove an IP from /etc/csf.allow and delete rule
-d, --deny ip       Deny an IP and add to /etc/csf.deny
-dr, --denyrm ip    Unblock an IP and remove from /etc/csf.deny
-df, --denyf        Remove and unblock all entries in /etc/csf.deny
-g, --grep ip       Search the iptables rules for an IP match (incl. CIDR)
-t, --temp          Displays the current list of temp IP entries and their TTL
-tr, --temprm ip    Remove an IPs from the temp IP ban and allow list
-td, --tempdeny ip ttl [-p port] [-d direction]
                    Add an IP to the temp IP ban list. ttl is how long to
                    blocks for (default:seconds, can use one suffix of h/m/d).
                    Optional port. Optional direction of block can be one of:
                    in, out or inout (default:in)
-ta, --tempallow ip ttl [-p port] [-d direction]
                    Add an IP to the temp IP allow list (default:inout)
-tf, --tempf        Flush all IPs from the temp IP entries
-cp, --cping        PING all members in an lfd Cluster
-cd, --cdeny ip     Deny an IP in a Cluster and add to /etc/csf.deny
-ca, --callow ip    Allow an IP in a Cluster and add to /etc/csf.allow
-cr, --crm ip       Unblock an IP in a Cluster and remove from /etc/csf.deny
-cc, --cconfig [name] [value]
                    Change configuration option [name] to [value] in a Cluster
-cf, --cfile [file] Send [file] in a Cluster to /etc/csf/
-crs, --crestart    Cluster restart csf and lfd
-w, --watch ip      Log SYN packets for an IP across iptables chains
-m, --mail [addr]   Display Server Check in HTML or email to [addr] if present
-lr, --logrun       Initiate Log Scanner report via lfd
-c, --check         Check for updates to csf but do not upgrade
-u, --update        Check for updates to csf and upgrade if available
-uf                 Force an update of csf
-x, --disable       Disable csf and lfd
-e, --enable        Enable csf and lfd if previously disabled
-v, --version       Show csf version

The command line tool will also tell you if the testing mode is enabled (which is a very useful feature). If TESTING were enabled, we'd see this line at the bottom of the output:

*WARNING* TESTING mode is enabled - do not forget to disable it in the configuration

Did you make it all the way through?! Great! I know it's a lot to take in, but it's not terribly complicated when we break it down and understand how each piece works. Next time, I'll be back with some tips on integrating CSF into cPanel.


March 5, 2013

Startup Series: Kickback Tickets

The very first client I recruited to Catalyst when I joined the CommDev team about a year ago happens to be one of Catalyst's most interesting customer success stories ... and I'm not just saying that because it was the first partner I signed on. Kickback Tickets — an online ticketing platform that utilized crowdfunding — has simplified the process of creating and funding amazing events, and as a result, they've made life a lot easier for the startup, developer and networking organizations that fuel Catalsyt.

Anyone who's organized events knows that it often involves a financial risk because it's hard to know whether the event will be well-enough attended to cover the costs of putting on the event. With Kickback Tickets, an event is listed an funded ahead of time, and when it reaches its "Tipping Point" goal of tickets ordered, it's completely funded, the early supporters are charged, and the ticket sales continue.

The process is simple:

Kickback Tickets

Event updates, guest registrations and QR-coded tickets are provided to attendees to make check-in seamless, so the hosts of each event don't have hassle with those details. Kickback's revenue comes from a small fee on each ticket for each successfully funded event, and they've got a ton of momentum. After signing on with Catalyst in March 2012, Kickback went live with an open beta in November 2012, and they launched their out-of-beta site in February 2013. They've successfully funded more than 20 events, and new events are added daily.

Kickback Tickets

When I met the Kickback founders Jonathan Perkins and Julian Balderas, I was attending SF Beta (my first official event as a SLayer). At the time, Jonathan and Julian were a couple of bankers with an innovative idea to help organizations alleviate the financial risk of planning and putting on events by enlisting community support. I told them about my experience as the COO of a small non-profit startup up called Slavery Footprint (also a Catalyst partner), and I guess they could relate to the challenges SoftLayer helped us overcome because they were excited to join.

In their own words, Jonathan and Julian explain that their partnership with Softlayer and the Catalyst program has been extremely valuable:

SoftLayer provides a rock-solid technical foundation and allows us to focus more resources on business development. On the technical side, what Softlayer offers is impressive — super fast speeds and an intricate level of control over the hardware. On the personal side, the mentorship and networking benefits of the program have been very helpful. We've always found the Catalyst team to be available to chat about any questions we had, ranging from development to biz dev to fundraising.

As they continue to expand their platform, it's going to be exciting to watch Kickback become a true force in the events space. Organize your next event with Kickback and make sure it's a success.

Oh, and if you want to speak to Jonathan and Julian, just reach out to me and I'll happily make the introduction.


February 20, 2013

Global Game Jam: Build a Video Game in 48 Hours

You're a conflicted zombie that yearns to be human again. Now you've got to dodge grandma and babies in an 8-bit side-scroller. Now you're Vimberly Koll, and you have to stop Poseidon from raining down on the Global Game Jam. At the end of Global Game Jam Vancouver, teams of developers, 3D artists, level designers and sound engineers conceptualized and created these games (along with a number of others) in less than 48 hours. Building a game in a weekend is no small task, so only the best and brightest game developers in the world converge on over 300 sites in 63 countries to show off their skills.

For the fifth annual Global Game Jam, more than 16,000 participants committed a weekend to learning from and collaborating with their peers in a worldwide game development hackathon. I was lucky enough to get to sit in on the action in Vancouver, and I thought I'd give you a glimpse into how participants make game development magic happen in such a short period of time.

Vancouver Global Game Jam

Day 1 (Friday Night): The Brainstorm
More than 260 participants poured into an open study area of the Life Sciences building at the Univerity of British Columbia to build the next best distraction ... er, video game. The event kicked off with a keynote from Brian Proviciano, a game development prodigy, who shared his history and offered sage advice for those interested in the industry. Following a comical 20-second idea pitch session, the caffeine began to flow and the brainstorm commenced.

Inspiration could come from anywhere, and a perfect example is the "Poseidon" game I mentioned above: GGJVancouver organizer Kimberly Voll had sprinklers rain on her office a few days prior to the event, so someone decided to make a game out of that situation. This year, the Global Game Jam introduced an interesting twist that they called "diversifiers." Diversifiers are side-challenges for extra credit, and two of my favorites were "Atari Age" — the game has to be smaller than 4kb — and "May the (Web) Force be With You" — the game has to run in a browser.

Fast-forward two hours, and as you look around, you see storyboards and scripts being written, characters being born, and a few intrepid developers starting to experiment with APIs, game engines , and external controllers to find some additional flair for their final products. You wouldn't expect a game made in 48 hours to incorporate an iOS Eye Tracking API or the Leap Motion gesture controller, but these developers are ambitious!

As the concepts are finalized, team members rotate in and out for sleep, and some even go home to get some rest — a good idea on the first night since everyone usually pulls an all-nighter on Saturday.

Vancouver Global Game Jam

Day 2 (Saturday): Laying the Foundation
It was cool to walk the aisles and peer over peoples' shoulders as musical scores, wrangled code and character models were coming together. However, the scene wasn't all smiles and hugs; a few groups were wrestling quirky bugs and integration issues, and in some cases, they ended up having to completely reboot their approach. Day 2 set the course for all of the teams. A few teams disbanded due to disagreements or unfixable bugs, and some developers peeled off from their teams to follow an untamed passion. In the Global Game Jam, there are no rules ... only games.

Vancouver Global Game Jam

Day 3 (Sunday): Sleep, What's That?
By Day 3, the building starts feeling like a college dorm during finals week when everyone is staying up all night to study or finish their comp-sci assignments (I know it wasn't just me...). Running on various vehicles of caffeine, teams worked heads-down all day to meet their 3pm deadline. Sighs of relief and high fives were exchanged when the games were submitted, and the event concluded with a pizza party and demo session where everyone could see and share the fruits of their labor.

Vancouver Global Game Jam

As I left the conference, teams were given the opportunity to showcase their games on the big screen to a chorus of laughter and applause. It was an awesome experience, and I'm glad SoftLayer sponsored it so that I could attend, take it all in and meet a ton of outstanding up-and-coming game developers. If you're into making games (or you've thought about it), check out the Global Game Jam one of these years.

Just don't forget to bring deodorant ... for your neighbor's sake.


Photo Credit Shout-Outs: Alex Larente, Ligia Brosch, Naz Madani. Great shots!

February 14, 2013

Tips and Tricks – Building a jQuery Plugin (Part 2)

jQuery plugins don't have to be complicated to create. If you've stumbled upon this blog in pursuit of a guide to show you how to make a jQuery plugin, you might not believe me ... It seems like there's a chasm between the "haves" of jQuery plugin developers and the "have nots" of future jQuery developers, and there aren't very many bridges to get from one side to the other. In Part 1 of our "Building a jQuery Plugin" series, we broke down how to build the basic structure of a plugin, and in this installment, we'll be adding some usable functionality to our plugin.

Let's start with the jQuery code block we created in Part 1:

(function($) {
    $.fn.slPlugin = function(options) {
            var defaults = {
                myVar: "This is", // this will be the default value of this var
                anotherVar: "our awesome",
                coolVar: "plugin!",
            var options = $.extend(defaults, options);
            this.each(function() {
                ourString = myVar + " " + anotherVar + " " + coolVar;
            return ourString;
}) (jQuery);

We want our plugin to do a little more than return, "This is our awesome plugin!" so let's come up with some functionality to build. For this exercise, let's create a simple plugin that allows truncates a blob of text to a specified length while providing the user an option show/hide the rest of the text. Since the most common character length limitation on the Internet these days is Twitter's 140 characters, we'll use that mark in our example.

Taking what we know about the basic jQuery plugin structure, let's create the foundation for our new plugin — slPlugin2:

(function($) {
    $.fn.slPlugin2 = function(options) {
        var defaults = {
            length: 140,
            moreLink: "read more",
            lessLink: "collapse",
            trailingText: "..."
        var options = $.extend(defaults, options);

As you can see, we've established four default variables:

  • length: The length of the paragraph we want before we truncate the rest.
  • moreLength: What we append to the paragraph when it is truncated. This will be the link the user clicks to expand the rest of the text.
  • lessLink: What we append to the paragraph when it is expanded. This will be the link the user clicks to collapse the rest of the text.
  • trailingText: The typical ellipses to append to the truncation.

In our jQuery plugin example from Part 1, we started our function with this.each(function() {, and for this example, we're going to add a return for this to maintain chainability. By doing so, we're able to manipulate the segment with methods. For example, if we started our function with this.each(function() {, we'd call it with this line:


If we start the function with return this.each(function() {, we have the freedom to add further manipulation:


With such a simple change, we're able to add method calls to make one massive dynamic function.

Let's flesh out the actual function a little more. We'll add a substantial bit of code in this step, but you should be able to follow along with the changes via the comments:

(function($) {
    $.fn.slPlugin2 = function(options) {
        var defaults = {
            length: 140, 
            moreLink: "read more",
            lessLink: "collapse",
            trailingText: "..."
        var options = $.extend(defaults, options);
        // return this keyword for chainability
        return this.each(function() {
            var ourText = $(this);  // the element we want to manipulate
            var ourHtml = ourText.html(); //get the contents of ourText!
            // let's check if the contents are longer than we want
            if (ourHtml.length > options.length) {
                var truncSpot = ourHtml.indexOf(' ', options.length); // the location of the first space (so we don't truncate mid-word) where we will end our truncation.
   // make sure to ignore the first space IF the text starts with a space
   if (truncSpot != -1){
       // the part of the text that will not be truncated, starting from the beginning
       var firstText = ourHtml.substring(0, truncSpot);
       // the part of the text that will be truncated, minus the trailing space
       var secondText = ourHtml.substring(truncSpot, ourHtml.legnth -1);

Are you still with us? I know it seems like a lot to take in, but each piece is very straightforward. The firstText is the chunk of text that will be shown: The first 140 characters (or whatever length you define). The secondText is what will be truncated. We have two blobs of text, and now we need to make them work together:

(function($) {
    $.fn.slPlugin2 = function(options) {
        var defaults = {
            length: 140, 
            moreLink: "read more",
            lessLink: "read less",
            trailingText: "..."
        var options = $.extend(defaults, options);
        // return this keyword for chainability
        return this.each(function() {
            var ourText = $(this);  // the element we want to manipulate
            var ourHtml = ourText.html(); //get the contents of ourText!
            // let's check if the contents are longer than we want
            if (ourHtml.length > options.length) {
                var truncSpot = ourHtml.indexOf(' ', options.length); // the location of the first space (so we don't truncate mid-word) where we will end our truncation.
   // make sure to ignore the first space IF the text starts with a space
   if (truncSpot != -1){
       // the part of the text that will not be truncated, starting from the beginning
       var firstText = ourHtml.substring(0, truncSpot);
       // the part of the text that will be truncated, minus the trailing space
       var secondText = ourHtml.substring(truncSpot, ourHtml.legnth -1);
       // perform our truncation on our container ourText, which is technically more of a "rewrite" of our paragraph, to our liking so we can modify how we please. It's basically saying: display the first blob then add our trailing text, then add our truncated part wrapped in span tags (to further modify)
       ourText.html(firstText + options.trailingText + '<span class="slPlugin2">' + secondText + '</span>');
       // but wait! The secondText isn't supposed to show until the user clicks "read more", right? Right! Hide it using the span tags we wrapped it in above.
       ourText.find('.slPlugin2').css("display", "none");

Our function now truncates text to the specified length, and we can call it from our page simply:

<script src="jquery.min.js"></script>
<script src="jquery.slPlugin2.js"></script>
<script type="text/javascript">
$(document).ready(function() {  

Out of all the ways to truncate text via jQuery, this has to be my favorite. It's feature-rich while still being fairly easy to understand. As you might have noticed, we haven't touched on the "read more" and "read less" links or the expanding/collapsing animations yet, but we'll be covering those in Part 3 of this series. Between now and when Part 3 is published, I challenge you to think up how you'd add those features to this plugin as homework.


December 27, 2012

Using SoftLayer Object Storage to Back Up Your Server

Before I came to my senses and moved my personal servers to SoftLayer, I was one of many victims of a SolusVM exploit that resulted in the wide-scale attack of many nodes in my previous host's Chicago data center. While I'm a firm believer in backing up my data, I could not have foreseen the situation I was faced with: Not only was my server in one data center compromised with all of its data deleted, but my backup server in one of the host's other data centers was also attacked ... This left me with old, stale backups on my local computer and not much else. I quickly relocated my data and decided that I should use SoftLayer Object Storage to supplement and improve upon my backup and disaster recovery plans.

With SoftLayer Object Storage Python Client set up and the SoftLayer Object Storage Backup script — — in hand, I had the tools I needed to build a solid backup infrastructure easily. On, I contributed an article about how to perform MySQL backups with those resources, so the database piece is handled, but I also need to back up my web files, so I whipped up another quick bash script to run:

# The path the backups will be dumped to
# Path to the web files to be backed up
BACKUP_PATH="/var/www/sites /"
# Back up folder name (mmddyyyy)
BACKUP_DIR="`date +%m%d%Y`"
# Backup File Name
DUMP_FILE="`date +%m_%d_%Y_%H_%M_%S`_site_files"
# SL container name
# Create backup dir if doesn't exist
if [ ! -d $DUMP_DIR$BACKUP_DIR ]; then
        mkdir -p $DUMP_DIR$BACKUP_DIR
# Make sure the archive exists
if [ -f $DUMP_DIR$BACKUP_DIR/$DUMP_FILE.tar.gz ]; then
        /root/ -s $DUMP_DIR$BACKUP_DIR/ -o "$CONTAINER" -r 30
        # Remove the backup stored locally
        rm -rf $DUMP_DIR$BACKUP_DIR
        # Success
        exit 0
        echo "$DUMP_DIR$BACKUP_DIR/$DUMP_FILE.tar.gz does not exist."
        exit 1

It's not the prettiest bash script, but it gets the job done. By tweaking a few variables, you can easily generate backups for any important directory of files and push them to your SoftLayer Object Storage account. If you want to change the retention time of your backups to be longer or shorter, you can change the 30 after the –r in the line below to the number of days you want to keep each backup:

/root/ -s $DUMP_DIR$BACKUP_DIR/ -o "$CONTAINER" -r 30

I created a script for each website on my server, and I set a CRON (crontab –e) entry to run each one on Sundays staggered by 5 minutes:

5 1 * * 0  /root/bin/cron/CRON-site1.com_web_files > /dev/null
10 1 * * 0  /root/bin/cron/CRON-site2.com_web_files > /dev/null
15 1 * * 0  /root/bin/cron/CRON-site3.com_web_files > /dev/null 

If you're looking for an easy way to automate and solidify your backups, this little bit of code could make life easier on you. Had I taken the few minutes to put this script together prior to the attack I experienced at my previous host, I wouldn't have lost any of my data. It's easy to get lulled into "backup apathy" when you don't need your backups, but just because nothing *has* happened to your data doesn't mean nothing *can* happen to your data.

Take it from me ... Be over-prepared and save yourself a lot of trouble.


December 19, 2012

SoftLayer API: Streamline. Simplify.

Building an API is a bit of a balancing act. You want your API to be simple and easy to use, and you want it to be feature-rich and completely customizable. Because those two desires happen to live on opposite ends of the spectrum, every API finds a different stasis in terms of how complex and customizable they are. The SoftLayer API was designed to provide customers with granular control of every action associated with any product or service on our platform; anything you can do in our customer portal can be done via our API. That depth of functionality might be intimidating to developers looking to dive in quickly and incorporate the SoftLayer platform into their applications, so our development team has been working to streamline and simplify some of the most common API services to make them even more accessible.

SoftLayer API

To get an idea of what their efforts look like in practice, Phil posted an SLDN blog with a perfect example of how they simplified cloud computing instance (CCI) creation via the API. The traditional CCI ordering process required developers to define nineteen data points:

Domain name
Package Id
Location Id
Quantity to order
Number of cores
Amount of RAM
Remote management options
Port speeds
Public bandwidth allotment
Primary subnet size
Disk size
Operating system
VPN Management - Private Network
Vulnerability Assessments & Management

While each of those data points is straightforward, you still have to define nineteen of them. You have all of those options when you check out through our shopping cart, so it makes sense that you'd have them in the API, but when it comes to ordering through the API, you don't necessarily need all of those options. Our development team observed our customers' API usage patterns, and they created the slimmed-down and efficient SoftLayer_Virtual_Guest::createObject — a method that only requires seven data points:

Domain name
Number of cores
Amount of RAM
Hourly/monthly billing
Local vs SAN disk
Operating System

Without showing you a single line of code, you see the improvement. Default values were established for options like Port speeds and Monitoring based on customer usage patterns, and as a result, developers only have to provide half the data to place a new CCI order. Because each data point might require multiple lines of code, the volume of API code required to place an order is slimmed down even more. The best part is that if you find yourself needing to modify one of the now-default options like Port speeds or Monitoring, you still can!

As the development team finds other API services and methods that can be streamlined and simplified like this one, they'll ninja new solutions to make the API even more accessible. Have you tried coding to the SoftLayer API yet? If not, what's the biggest roadblock for you? If you're already a SLAPI coder, what other methods do you use often that could be streamlined?


November 27, 2012

Tips and Tricks - Building a jQuery Plugin (Part 1)

I've written several blogs detailing the use of different jQuery plugins (like Select2, LazyLoad and equalHeights), and in the process, I've noticed an increasing frustration among the development community when it comes to building jQuery plugins. The resources and documentation I've found online have not as clear and easy as they could be, so in my next few posts, I'll break down the process to make jQuery plugin creation simple and straightforward. In this post, we'll cover the basic structure of a plugin and where to insert your own functionality, and in Part 2, we'll pick a simple task and add on to our already-made structure.

Before I go any further, it's probably important to address a question you might be asking yourself: "Why would I want to make my own plugin?" The best reason that comes to my mind is portability. If you've ever created a large-scale project, take a look back into your source code and note how many of the hundreds of lines of jQuery code you could put into a plugin to reuse on a different project. You probably invested a lot of time and energy into that code, so it doesn't make sense to reinvent the wheel if you ever need that functionality again. If that's not enough of a reason for you, I can also tell you that if you develop your own jQuery plugin, you'll level-up in cool points, and the jQuery community will love you.

For this post, let's create a jQuery plugin that simply returns, "This is our awesome plugin!" Our first step involves putting together the basic skeleton used by every plugin:

(function($) {
    $.fn.slPlugin = function() {
            // Awesome plugin stuff goes here
}) (jQuery);

This is your template — your starting point. Practice it. Remember it. Love it. The "slPlugin" piece is what I chose to name this plugin. It's best to name your plugin something unique ... I always run a quick Google search to ensure I don't duplicate the name of a plugin I (or someone else) might need to use in a project alongside my plugin. In this case, we're calling the example plugin slPlugin because SoftLayer is awesome, and I like naming my plugins after awesome things. I'll save this code in a file called jquery.slPlugin.js.

Now that we have our plugin's skeleton, let's add some default values for variables:

(function($) {
    $.fn.slPlugin = function(options) {
            var defaults = {
                myVar: "default", // this will be the default value of this var
                anotherVar: 0,
                coolVar: "this is cool",                
            var options = $.extend(defaults, options);
}) (jQuery);

Let's look at the changes we made between the first example and this one. You'll notice that in our second line we added "options" to become $.fn.slPlugin = function(options) {. We do this because our function is now accepting arguments, and we need to let the function know that. The next difference you come across is the var defaults blurb. In this section, we're providing default values for our variables. If you don't define values for a given variable when you call the plugin, these default values will be used.

Now let's have our plugin return the message we want to send:

(function($) {
    $.fn.slPlugin = function(options) {
            var defaults = {
                myVar: "This is", // this will be the default value of this var
                anotherVar: "our awesome",
                coolVar: "plugin!",
            var options = $.extend(defaults, options);
            this.each(function() {
                ourString = myVar + " " + anotherVar + " " + coolVar;
            return ourString;
}) (jQuery);

We've defined our default values for our variables, concatenated our variables and we've added a return under our variable declaration. If our jQuery plugin is included in a project and no values are provided for our variables, slPlugin will return, "This is our awesome plugin!"

It seems rather rudimentary at this point, but we have to crawl before we walk. This introductory post is laying the groundwork of coding a jQuery plugin, and we'll continue building on this example in the next installment of this series. As you've seen with the LazyLoad, equalHeights and Select2, there are much more complicated things we can do with our plugin, and we'll get there. Sneak Preview: In the next installment, we'll be creating and implementing a truncation function for our plugin ... Get excited!


November 6, 2012

Tips and Tricks - Pure CSS Sticky Footers

By now, if you've seen my other blog posts, you know that I'm fascinated with how much JavaScript has evolved and how much you can do with jQuery these days. I'm an advocate of working smarter, not harder, and that maxim knows no coding language limits. In this post, I want to share a pure CSS solution that allows for "sticky" footers on a web page. In comparing several different techniques to present this functionality, I found that all of the other routes were overkill when it came to processing time and resource usage.

Our objective is simple: Make the footer of our web page stay at the bottom even if the page's content area is shorter than the user's browser window.

This, by far, is one of my *favorite* things to do. It makes the web layout so much more appealing and creates a very professional feel. I ended up kicking myself the very first time I tried to add this functionality to a project early in my career (ten years ago ... already!?) when I found out just how easy it was. I take solace in knowing that I'm not alone, though ... A quick search for "footer stick bottom" still yields quite a few results from fellow developers who are wrestling with the same frustrating experience I did. If you're in that boat, fear no more! We're going to your footers in shape in a snap.

Here's a diagram of the problem:

CSS Footer

Unfortunately, a lot of people try to handle it with setting a fixed height to the content which would push the footer down. This may work when YOU view it, but there are several different browser window heights, resolutions and variables that make this an *extremely* unreliable solution (notice the emphasis on the word "extremely" ... this basically means "don't do it").

We need a dynamic solution that is able to adapt on the fly to the height of a user's browser window regardless if the resize it, have Firebug open, use a unique resolution or just have a really, really weird browser!

Let's take a look at what the end results should look like:

CSS Footer

To make this happen, let's get our HTML structure in place first:

<div id="page">
      <div id="header"> </div>
      <div id="main"> </div>
      <div id="footer"> </div>

It's pretty simple so far ... Just a skeleton of a web page. The page div contains ALL elements and is immediately below the

tags in the page code hierarchy. The header div is going to be our top content, the main div will include all of our content, and the footer div is all of our copyrights and footer links.

Let's start by coding the CSS for the full page:

Html, body {
      Padding: 0;
      Margin: 0;
      Height: 100%;

Adding a 100% height allows us to set the height of the main div later. The height of a div can only be as tall as the parent element encasing it. Now let's see how the rest of our ids are styled:

#page {
      Min-height: 100%;
#main {
      Padding-bottom: 75px;   /* This value is the height of your footer */
#footer {
      Position: absolute;
      Width: 100%;
      Bottom: 0;
      Height: 75px;  /* This value is the height of your footer */

These rules position the footer "absolutely" at the bottom of the page, and because we set #page to min-height: 100%, it ensures that #main is exactly the height of the browser's viewing space. One of the best things about this little trick is that it's compliant with all major current browsers — including Firefox, Chrome, Safari *AND* Internet Explorer (after a little tweak). For Internet Explorer to not throw a fit, we need concede that IE doesn't recognize min-height as a valid property, so we have to add Height: 100%; to #page:

#page {
      Min-height: 100%;  /* for all other browsers */
      height: 100%;  /* for IE */

If the user does not have a modern, popular browser, it's still okay! Though their old browser won't detect the magic we've done here, it'll fail gracefully, and the footer will be positioned directly under the content, as it would have been without our little CSS trick.

I can't finish this blog without mentioning my FAVORITE perk of this trick: Should you not have a specially designed mobile version of your site, this trick even works on smart phones!


October 17, 2012

Tips and Tricks - jQuery Select2 Plugin

Web developers have the unique challenge of marrying coding logic and visual presentation to create an amazing user experience. Trying to find a balance between those two is pretty difficult, and it's easy to follow one or the other down the rabbit hole. What's a web developer to do?

I've always tried to go the "work smarter, not harder" route, and when it comes to balancing functionality and aesthetics, that usually means that I look around for plugins and open source projects that meet my needs. In the process of sprucing up an form, I came across jQuery Select2, and it quickly became one of my favorite plugins for form formatting. With minimal scripting and little modification, you get some pretty phenomenal results.

We've all encountered drop-down selection menus on web forms, and they usually look like this:

Option Select

Those basic drop-downs meet a developer's need for functionality, but they aren't winning any beauty pageants. Beyond the pure aesthetic concerns, when a menu contains dozens (or hundreds) of selectable options, it becomes a little unwieldy. That's why I was so excited to find Select2.

With Select2, you can turn the old, plain, boring-looking select boxes into beautiful, graceful and more-than-functional select widgets:

Pretty Option Select

Not only is the overall presentation of the data improved, Select2 also includes an auto-complete box. A user can narrow down the results quickly ad easily, and if you've got some of those endlessly scrolling select boxes of country names or currencies, your users will absolutely notice the change (and love you for it).

What's even sexier than the form facelift is that you can add the plugin to your form in a matter of minutes.

After we download Select2 and upload it to our box, we add our the jQuery library and scripts to the <head> of our document:

<script src="jquery.js" type="text/javascript"></script> 
<script src="select2.js" type="text/javascript"></script>

For the gorgeous styling, we'll also add Select2's included style sheet:

<link href="select2.css" rel="stylesheet"/>

Before we close our <head> tag, we invoke the Select2 function:

$(document).ready(function() { $("#selectPretty").select2(); });

At this point, Select2 is locked and load, and we just have to add the #selectPretty ID to the select element we want to improve:

<select id="selectPretty">
<option value="Option1">Option 1</option>
<option value="Option2">Option 2</option>
<option value="Option3">Option 3</option>
<option value="Option4">Option 4</option>

Notice: the selectPretty ID is what we defined when we invoked the Select2 function in our <head> tag.

With miniscule coding effort, we've made huge improvements to the presentation of our usually-boring select menu. It's so easy to implement that even the most black-and-white coding-minded web developers can add some pizzazz to their next form without having to get wrapped up in styling!


Subscribe to development